Codeforces Round #265 (Div. 1) E. The Classic Problem 最短路+线段树+hash

E. The Classic Problem

题意:有一个无向图,每条边的权值为2^x(0<=x<=1e5),求 s 到 t 的最短路。

思路:最短路好搞,关键是这个权值不好比较大小,由于x<=1e5,但是我们发现从一个点 u 到 v,其距离只增加了2^a[x],那么我们可以用可持久化线段树保存新增的这个信息,每个点的最短路都用一颗线段树保存,线段树第 i 个叶子节点保存的是是否有 2^i 的权值,那么我们怎么比较两个数大小呢?对于d[ u ],d[ v ],我从这两颗线段树同时往叶子节点找,从右区间开始,通过hash找到第一个不相等的点,就可以用log的复杂度判断两个数大小,还有一个问题,怎么更新线段树,假设线段树d[u]保存了11101(左边低位,右边高位),d[v]比d[u]多了2^0,那么我从第0位开始找到第一个为0的点 k  =3,然后把 [ 0 , 2 ]区间的1抹去,并把第3位的0修改为1就可以得到d[v]这颗线段树。

#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+30,mod=1e9+7,mod2=998244353;
int ls[maxn*30],rs[maxn*30],mn[maxn*30],sum[maxn*30];
int vis[maxn],P[maxn],path[maxn],cnt,mx=1e5+30;
int ha[maxn*30],p[maxn],sz[maxn];
vector<int>G[maxn],dis[maxn];
struct node
{
	int u,o;
	node(){}
	node(int a,int b){u=a,o=b;}
	bool operator<(const node&t)const
	{
		int l=1,r=mx,o1=o,o2=t.o;
		while(l!=r)
		{
			int m=(l+r)/2;
			if(ha[rs[o1]]!=ha[rs[o2]]||sum[rs[o1]]!=sum[rs[o2]])
			{
				l=m+1;
				o1=rs[o1];
				o2=rs[o2];
			}
			else
			{
				r=m;
				o1=ls[o1];
				o2=ls[o2];
			}
		}
		return ha[o1]>ha[o2];
	}
}d[maxn];
priority_queue<node>q;
int qu(int o,int l,int r,int ql,int qr)
{
	if(l>=ql&&r<=qr)return mn[o];
	int m=(l+r)/2,res=1e9;
	if(ql<=m)res=min(res,qu(ls[o],l,m,ql,qr));
	if(qr>m)res=min(res,qu(rs[o],m+1,r,ql,qr));
	return res;
}
void pushup(int o)
{
	mn[o]=min(mn[ls[o]],mn[rs[o]]);
	ha[o]=(1ll*ha[ls[o]]+ha[rs[o]])%mod2;
	sum[o]=(1ll*sum[ls[o]]+sum[rs[o]])%mod;	
}
void up(int &o,int pre,int l,int r,int k,int v)
{
	o=++cnt;
	ls[o]=ls[pre];
	rs[o]=rs[pre];
	if(l==r)
	{
		ha[o]=p[l]*v;
		sum[o]=P[l]*v;
		if(v)mn[o]=1e9;
		else mn[o]=l;
		return;
	}
	int m=(l+r)/2;
	if(k<=m)up(ls[o],ls[pre],l,m,k,v);
	else up(rs[o],rs[pre],m+1,r,k,v);
	pushup(o);
}
void up2(int &o,int pre,int l,int r,int ql,int qr)
{
	if(l>=ql&&r<=qr)
	{
		o=0;
		return;
	}
	o=++cnt;
	ls[o]=ls[pre];
	rs[o]=rs[pre];
	int m=(l+r)/2;
	if(ql<=m)up2(ls[o],ls[pre],l,m,ql,qr);
	if(qr>m)up2(rs[o],rs[pre],m+1,r,ql,qr);
	pushup(o);
}
int Query(int rt,int k)
{
	int l=k,r=mx;
	while(l<r)
	{
		int m=(l+r)/2;
		if(qu(rt,0,mx,k,m))
		l=m+1;
		else r=m;
	}
	return l;
}
int dij(int n,int s,int t)
{	
	for(int i=1;i<=n;i++)
	d[i].u=i,d[i].o=0;
	q.push(d[s]);
	sz[s]=1;
	while(!q.empty())
	{
		int u=q.top().u;q.pop();
		if(vis[u])continue;
		vis[u]=1;
		for(int i=0;i<G[u].size();i++)
		{
			int v=G[u][i],w=dis[u][i];
			if(vis[v])continue;
			node e=node(v,0);
			int k=Query(d[u].o,w);
			if(k>w)
			up2(e.o,d[u].o,0,mx,w,k-1);
			if(!e.o)up(e.o,d[u].o,0,mx,k,1);
			else up(e.o,e.o,0,mx,k,1);
			if(d[v].o&&(e<d[v]||(ha[e.o]==ha[d[v].o]&&sum[e.o]==sum[d[v].o])))
			continue;
			sz[v]=sz[u]+1;
			path[v]=u;
			d[v].o=e.o;
			q.push(d[v]);
		}
	}
	if(!vis[t])
	return -1;
	return  sum[d[t].o];
}
void init()
{
	p[0]=P[0]=1;
	for(int i=1;i<maxn;i++)
	{
		p[i]=1ll*p[i-1]*43%mod2;
		P[i]=1ll*P[i-1]*2%mod;
	}
}
void dfs(int u)
{
	if(!u)return;
	dfs(path[u]);
	printf("%d ",u);
}
int main()
{
	init();
	int n,m,u,v,w,s,t;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&u,&v,&w);
		G[u].push_back(v);
		G[v].push_back(u);
		dis[u].push_back(w);
		dis[v].push_back(w);
	}
	scanf("%d%d",&s,&t);
	int ans=dij(n,s,t);
	printf("%d\n",ans);
	if(ans!=-1)
	{
		printf("%d\n",sz[t]);
		dfs(t);
	}
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

长沙橘子猫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值