[主席树][哈希][最短路]CF464E The Classic Problem
题面
https://codeforces.com/contest/464/problem/E
https://www.luogu.com.cn/problem/CF464E
分析
高精度暴力跑最短路 $O(nlognx)$ ,考虑优化
发现最短路中松弛操作所用的加法,在二进制串上所需要实现的就是区间清零和单点修改
e.g. 01110+00100=10010
考虑用线段树维护01串,找一段连续 1 区间可以用线段树二分解决,比较大小的时候可以用类似字符串哈希的做法来确定高位是否相同
但时间复杂度 $O(nlogx)$ 空间复杂度 $O(n^2logx)$ 不能通过
考虑到有许多共用节点,将线段树改为主席树
区间清零可以通过建一个全零线段树,将需要清零的区间的父亲连到对应零区间,辅以一些回收节点的操作即可通过
时间复杂度 $O(nlogx)$ 空间复杂度 $O(nlog^2x)$
代码
#include <iostream> #include <cstdio> #include <queue> using namespace std; typedef long long ll; typedef unsigned long long ull; const ll P=1e9+7; const int N=2e5+10; const int M=N*20; struct Graph { int v,w,nx; }g[2*N]; int cnt,list[N],tcnt,root[N],f[N]; struct Node { ll val; ull hash; int cnt,c[2]; friend bool operator == (Node a,Node b) {return a.val==b.val&&a.hash==b.hash&&a.cnt==b.cnt;} }t[2*M]; ll pw[N]; ull hnum[N]; bool vis[N]; int n,m,inf,S,T,ans[N],acnt; void Add(int u,int v,int w) {g[++cnt]=(Graph){v,w,list[u]};list[u]=cnt;} void Update(int x) { t[x].cnt=t[t[x].c[0]].cnt+t[t[x].c[1]].cnt; t[x].val=(t[t[x].c[0]].val+t[t[x].c[1]].val)%P; t[x].hash=t[t[x].c[0]].hash+t[t[x].c[1]].hash; } void Build(int &x,int l,int r,int val) { if (!x) x=++tcnt; if (l==r) return t[x].cnt=val,t[x].val=val*pw[l],t[x].hash=val*hnum[l],void(); int mid=l+r>>1; Build(t[x].c[0],l,mid,val);Build(t[x].c[1],mid+1,r,val); Update(x); } int Query(int x,int l,int r,int L,int R) { if (L<=l&&r<=R) return t[x].cnt; int mid=l+r>>1,ans=0; if (L<=mid) ans=Query(t[x].c[0],l,mid,L,R); if (mid<R) ans+=Query(t[x].c[1],mid+1,r,L,R); return ans; } int Search(int x,int l,int r,int k) { if (l==r) return l; int mid=l+r>>1; if (k>mid) return Search(t[x].c[1],mid+1,r,k); if (Query(t[x].c[0],l,mid,k,mid)==mid-k+1) return Search(t[x].c[1],mid+1,r,mid+1); return Search(t[x].c[0],l,mid,k); } bool S_CMP(int x,int y,int l,int r) { if (l==r) return t[x].cnt<t[y].cnt; int mid=l+r>>1; if (t[t[x].c[1]]==t[t[y].c[1]]) return S_CMP(t[x].c[0],t[y].c[0],l,mid); return S_CMP(t[x].c[1],t[y].c[1],mid+1,r); } void Link(int &x,int y,int l,int r,int L,int R) { if (L<=l&&r<=R) return x=y,void(); int mid=l+r>>1,z=++tcnt;t[z]=t[x]; if (L<=mid) Link(t[z].c[0],t[y].c[0],l,mid,L,R); if (mid<R) Link(t[z].c[1],t[y].c[1],mid+1,r,L,R); Update(x=z); } void S_Add(int &x,int l,int r,int k) { t[++tcnt]=t[x];x=tcnt; if (l==r) return t[x].val=pw[l],t[x].cnt=1,t[x].hash=hnum[l],void(); int mid=l+r>>1; if (k<=mid) S_Add(t[x].c[0],l,mid,k); else S_Add(t[x].c[1],mid+1,r,k); Update(x); } struct Path { int id,rt; friend bool operator < (Path a,Path b) {return S_CMP(b.rt,a.rt,0,N-1);} }; priority_queue<Path> q; void Dijkstra() { priority_queue<Path> q; while (!q.empty()) q.pop(); for (int i=1;i<=n;i++) root[i]=inf;root[S]=root[0];q.push((Path){S,root[S]}); while (!q.empty()) { int u=q.top().id;q.pop(); if (vis[u]) continue;vis[u]=1; for (int i=list[u];i;i=g[i].nx) { int newz=root[u],l=Search(root[u],0,N-1,g[i].w),re=tcnt; S_Add(newz,0,N-1,l); if (g[i].w<l) Link(newz,root[0],0,N-1,g[i].w,l-1); if (S_CMP(newz,root[g[i].v],0,N-1)) f[g[i].v]=u,q.push((Path){g[i].v,root[g[i].v]=newz}); else tcnt=re; } } } int main() { scanf("%d%d",&n,&m); for (int i=1,u,v,x;i<=m;i++) scanf("%d%d%d",&u,&v,&x),Add(u,v,x),Add(v,u,x); scanf("%d%d",&S,&T); pw[0]=1;hnum[0]=1;for (int i=1;i<N;i++) pw[i]=(pw[i-1]<<1)%P,hnum[i]=hnum[i-1]*23; Build(root[0],0,N-1,0);Build(inf,0,N-1,1); Dijkstra(); if (root[T]==inf) return printf("-1\n"),0; for (int x=T;x!=S;x=f[x]) ans[++acnt]=x; ans[++acnt]=S; printf("%lld\n%d\n",t[root[T]].val,acnt); for (int i=acnt;i;i--) printf("%d ",ans[i]); }
在日渐沉没的世界里,我发现了你。