「BZOJ3694」「FJ2014集训」最短路
首先树剖没得说了,这里说一下并查集的做法,
对于一条非树边,它会影响的点就只有u(i),v(i)到lca,对于lca-v的路径上所有点x,都可通过1-t-u-v-x,长度为dep[u]+dep[v]+w(i)-dep[x],lca-u同理,
将非树边按dep[u]+dep[v]+w(i)从小到大排序,显然每个点被前一条能更新他的边更新后即是最优解,此时将它与父亲节点合并,修改的时候用并查集向上修改即可。
#include<algorithm> #include<iostream> #include<cstring> #include<cstdio> #include<queue> #define MP(a,b) make_pair(a,b) #define MAXN 1000000 using namespace std; int fa[MAXN],f[MAXN][20],dep[MAXN]; struct edge { int u,v,w,is,nxt; #define u(x) ed[x].u #define v(x) ed[x].v #define w(x) ed[x].w #define is(x) ed[x].is #define n(x) ed[x].nxt friend bool operator < (edge a,edge b) { return a.is==b.is?(dep[a.u]+dep[a.v]+a.w)<(dep[b.u]+dep[b.v]+b.w):a.is<b.is; } }ed[MAXN]; int first[MAXN],num_e; #define f(x) first[x] int n,m,n0; inline void add_e(int u,int v,int w,int t); int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);} void dfs(int x,int ff,int deep) { f[x][0]=ff;dep[x]=deep; for(int i=f(x);i;i=n(i)) if(v(i)!=ff && is(i)) dfs(v(i),x,deep+w(i)); } int dis[MAXN]; signed main() { // freopen("in.txt","r",stdin); scanf("%d%d",&n,&m); for(int i=1;i<=n;i++)fa[i]=i; int ai,bi,wi,ti; for(int i=1;i<=m;i++) { scanf("%d%d%d%d",&ai,&bi,&wi,&ti); add_e(ai,bi,wi,ti),add_e(bi,ai,wi,ti); if(!ti)n0+=2; } dfs(1,0,0); for(int i=1;i<20;i++) for(int j=1;j<=n;j++) f[j][i]=f[f[j][i-1]][i-1]; sort(ed+1,ed+m*2+1);dis[1]=0; for(int i=1;i<=n0;i++) { int x=u(i),y=v(i); while(x!=y) { if(dep[x]<dep[y])swap(x,y); if(!dis[x])dis[x]=dep[u(i)]+dep[v(i)]+w(i)-dep[x]; fa[x]=f[x][0]; x=fa[x]=find(fa[x]); } } for(int i=2;i<=n;i++) printf("%d ",!dis[i]?-1:dis[i]); } inline void add_e(int u,int v,int w,int t) { ++num_e; u(num_e)=u; v(num_e)=v; w(num_e)=w; is(num_e)=t; n(num_e)=f(u); f(u)=num_e; }
波澜前,面不惊。