BZOJ2725: [Violet 6]故乡的梦
$n \leq 2e5,m \leq 2e5,q \leq 2e5$,$n,m$的图给固定的$s,t$,以及$q$个询问,每次问删掉某条边之后问$s$到$t$的最短路。
首先喷一波,出题人语文太差了,这么美的题目居然潦草两句带过背景,描述题目也毫无趣味,差评!(本题分类:语文)
方法一:把图载到一个可控的数据结构上进行信息维护。删边之后,如果删掉的不是$s$到$t$的最短路,那直接输出最短路,否则,可以观察到一条非最短路边$w(x,y)$的贡献是$ds(x)+w(x,y)+dt(y)$,其中$ds,dt$表示起点到其最短路和终点到其最短路。
好的那把最短路树建出来吧,然后把这些边的贡献载到点上,相当于链取$min$,单点查询。
细节:
对一个在$z$处的询问查询它爸爸那条边断掉的情况,在其子树中,如果$lca(x,z)$深度比$lca(y,z)$大,那么$ds(x)+w(x,y)+dt(y)$是没贡献的,因为这个贡献实际上是这样的:$s->lca(x,z)->x->y->lca(y,z)->t$。
然后链取$min$改成子树标记,用线段树合并维护之。
结果:慢,丑。
1 //#include<iostream> 2 #include<cstring> 3 #include<cstdlib> 4 #include<cstdio> 5 #include<queue> 6 //#include<math.h> 7 //#include<time.h> 8 #include<assert.h> 9 //#include<complex> 10 #include<algorithm> 11 using namespace std; 12 13 int n,m,s,t,q; 14 #define LL long long 15 #define maxn 200011 16 #define maxm 400011 17 struct Edge{int to; int v,next;}; 18 int fa[maxn],ll[maxn],rr[maxn],anc[maxn][19],dep[maxn],Time; 19 struct qnode 20 { 21 LL v; int id; 22 bool operator > (const qnode &b) const {return v>b.v;} 23 }; 24 priority_queue<qnode,vector<qnode>,greater<qnode> > que; 25 struct Graph 26 { 27 Edge edge[maxm]; int first[maxn],le; 28 Graph() {le=2;} 29 void in(int x,LL y,int v) {Edge &e=edge[le]; e.to=y; e.v=v; e.next=first[x]; first[x]=le++;} 30 void insert(int x,int y,int v) {in(x,y,v); in(y,x,v);} 31 void dijkstra(LL *dis,int s) 32 { 33 que.push((qnode){0,s}); 34 for (int i=1;i<=n;i++) dis[i]=1e18; dis[s]=0; fa[s]=0; 35 while (!que.empty()) 36 { 37 int now=que.top().id; LL d=que.top().v; que.pop(); 38 if (dis[now]!=d) continue; 39 for (int i=first[now];i;i=edge[i].next) 40 { 41 Edge &e=edge[i]; 42 if (dis[e.to]>dis[now]+e.v) 43 { 44 dis[e.to]=dis[now]+e.v; 45 fa[e.to]=now; 46 que.push((qnode){dis[e.to],(int)e.to}); 47 } 48 } 49 } 50 } 51 void dfs(int x) 52 { 53 anc[x][0]=fa[x]; ll[x]=++Time; dep[x]=dep[fa[x]]+1; 54 for (int i=first[x];i;i=edge[i].next) dfs(edge[i].to); 55 rr[x]=Time; 56 } 57 }g,tree,eve,fl; 58 void makeanc() 59 { 60 for (int j=1;j<=18;j++) 61 for (int i=1;i<=n;i++) 62 anc[i][j]=anc[anc[i][j-1]][j-1]; 63 } 64 int lca(int x,int y) 65 { 66 if (dep[x]<dep[y]) {int t=x; x=y; y=t;} 67 for (int j=18;j>=0;j--) if (dep[anc[x][j]]>=dep[y]) x=anc[x][j]; 68 if (x==y) return x; 69 for (int j=18;j>=0;j--) if (anc[x][j]!=anc[y][j]) x=anc[x][j],y=anc[y][j]; 70 return anc[x][0]; 71 } 72 LL diss[maxn],dist[maxn]; 73 LL ans[maxn],lisa[maxm]; int li=0; 74 75 int root[maxn]; 76 struct smt 77 { 78 struct Node 79 { 80 int ls,rs; 81 int cnt,Min; 82 }a[maxn*18]; 83 int n,size; 84 void clear(int m) {size=0; n=m; a[0].Min=n+1;} 85 void up(int x) 86 { 87 int &p=a[x].ls,&q=a[x].rs; 88 a[x].Min=min(a[p].Min,a[q].Min); 89 } 90 void New(int &x) {x=++size; a[x].ls=a[x].rs=0; a[x].cnt=0; a[x].Min=n+1;} 91 void insert(int &x,int L,int R,int pos,int v) 92 { 93 if (!x) New(x); 94 if (L==R) 95 { 96 a[x].cnt+=v; 97 if (a[x].cnt>0) a[x].Min=pos; 98 else a[x].Min=n+1; 99 return; 100 } 101 int mid=(L+R)>>1; 102 if (pos<=mid) insert(a[x].ls,L,mid,pos,v); else insert(a[x].rs,mid+1,R,pos,v); 103 up(x); 104 } 105 void insert(int &x,int pos,int v) {insert(x,1,n,pos,v);} 106 void combine(int &x,int y,int L,int R) 107 { 108 if (!x || !y) {x=x+y; return;} 109 if (L==R) {a[x].cnt=a[x].cnt+a[y].cnt; if (a[x].cnt>0) a[x].Min=L; else a[x].Min=n+1; return;} 110 int mid=(L+R)>>1; 111 combine(a[x].ls,a[y].ls,L,mid); 112 combine(a[x].rs,a[y].rs,mid+1,R); 113 up(x); 114 } 115 void combine(int &x,int y) {combine(x,y,1,n);} 116 }smt; 117 118 void go(int x,int fa) 119 { 120 root[x]=0; 121 for (int i=fl.first[x];i;i=fl.edge[i].next) 122 { 123 Edge &e=fl.edge[i]; 124 smt.insert(root[x],e.to,e.v); 125 } 126 for (int i=tree.first[x];i;i=tree.edge[i].next) 127 { 128 Edge &e=tree.edge[i]; if (e.to==fa) continue; 129 go(e.to,x); smt.combine(root[x],root[e.to]); 130 } 131 for (int i=eve.first[x];i;i=eve.edge[i].next) 132 { 133 Edge &e=eve.edge[i]; 134 ans[e.to]=lisa[smt.a[root[x]].Min]; 135 } 136 } 137 138 int lx[maxm],ly[maxm]; 139 int main() 140 { 141 scanf("%d%d",&n,&m); 142 for (int i=1,x,y,v;i<=m;i++) scanf("%d%d%d",&x,&y,&v),g.insert(x,y,v); 143 scanf("%d%d",&s,&t); 144 g.dijkstra(dist,t); g.dijkstra(diss,s); 145 for (int i=1;i<=n;i++) if (fa[i]) tree.in(fa[i],i,0); 146 tree.dfs(s); makeanc(); 147 for (int i=2;i<g.le;i+=2) 148 { 149 int x=g.edge[i].to,y=g.edge[i^1].to; lx[i]=lca(x,t),ly[i]=lca(y,t); 150 if (fa[x]==y || fa[y]==x) continue; 151 LL tmp=diss[x]+dist[y]+g.edge[i].v; 152 if (dep[ly[i]]>dep[lx[i]]) {lisa[++li]=tmp;} 153 tmp=diss[y]+dist[x]+g.edge[i].v; 154 if (dep[lx[i]]>dep[ly[i]]) {lisa[++li]=tmp;} 155 assert(li<maxm); 156 } 157 lisa[0]=-1e18; sort(lisa+1,lisa+1+li); li=unique(lisa+1,lisa+1+li)-lisa-1; lisa[li+1]=1e18; 158 smt.clear(li); 159 for (int i=2;i<g.le;i+=2) 160 { 161 int x=g.edge[i].to,y=g.edge[i^1].to; 162 if (fa[x]==y || fa[y]==x) continue; 163 LL tmp=diss[x]+dist[y]+g.edge[i].v; int tt=lower_bound(lisa+1,lisa+1+li,tmp)-lisa; 164 if (dep[ly[i]]>dep[lx[i]]) {fl.in(y,tt,1); fl.in(x,tt,-1);} 165 tmp=diss[y]+dist[x]+g.edge[i].v; tt=lower_bound(lisa+1,lisa+1+li,tmp)-lisa; 166 if (dep[lx[i]]>dep[ly[i]]) {fl.in(x,tt,1); fl.in(y,tt,-1);} 167 } 168 169 scanf("%d",&q); 170 for (int i=1,x,y;i<=q;i++) 171 { 172 scanf("%d%d",&x,&y); 173 if (fa[x]!=y && fa[y]!=x) ans[i]=diss[t]; 174 else if (ll[x]<=ll[t] && ll[t]<=rr[x] && ll[y]<=ll[t] && ll[t]<=rr[y]) 175 { 176 if (fa[x]==y) eve.in(x,i,0); 177 else eve.in(y,i,0); 178 } 179 else ans[i]=diss[t]; 180 } 181 for (int i=tree.first[s];i;i=tree.edge[i].next) 182 { 183 Edge &e=tree.edge[i]; 184 if (ll[e.to]<=ll[t] && ll[t]<=rr[e.to]) {go(e.to,s); break;} 185 } 186 for (int i=1;i<=q;i++) printf(ans[i]==1e18?"Infinity\n":"%lld\n",ans[i]); 187 return 0; 188 }
方法二:可以发现刚刚的一系列行为都在$s->t$这条链上进行。实际上一条边$w(x,y)$的贡献可以看成:$s->s'->x->y->t'->t$,因此$s'$到$t'$之间的路都可以被贡献到,可以区间打标记。维护一条链比树上操作要方便许多。区间取$min$单点查询,可以线段树,也可排序+并查集。
代码?略。