树形结构的维护:BZOJ 3991: [SDOI2015]寻宝游戏
Description
小B最近正在玩一个寻宝游戏,这个游戏的地图中有N个村庄和N-1条道路,并且任何两个村庄之间有且仅有一条路径可达。游戏开始时,玩家可以任意选择一个村庄,瞬间转移到这个村庄,然后可以任意在地图的道路上行走,若走到某个村庄中有宝物,则视为找到该村庄内的宝物,直到找到所有宝物并返回到最初转移到的村庄为止。小B希望评测一下这个游戏的难度,因此他需要知道玩家找到所有宝物需要行走的最短路程。但是这个游戏中宝物经常变化,有时某个村庄中会突然出现宝物,有时某个村庄内的宝物会突然消失,因此小B需要不断地更新数据,但是小B太懒了,不愿意自己计算,因此他向你求助。为了简化问题,我们认为最开始时所有村庄内均没有宝物
Input
第一行,两个整数N、M,其中M为宝物的变动次数。
接下来的N-1行,每行三个整数x、y、z,表示村庄x、y之间有一条长度为z的道路。
接下来的M行,每行一个整数t,表示一个宝物变动的操作。若该操作前村庄t内没有宝物,则操作后村庄内有宝物;若该操作前村庄t内有宝物,则操作后村庄内没有宝物。
Output
M行,每行一个整数,其中第i行的整数表示第i次操作之后玩家找到所有宝物需要行走的最短路程。若只有一个村庄内有宝物,或者所有村庄内都没有宝物,则输出0。
Sample Input
4 5
1 2 30
2 3 50
2 4 60
2
3
4
2
1
1 2 30
2 3 50
2 4 60
2
3
4
2
1
Sample Output
0
100
220
220
280
100
220
220
280
HINT
1<=N<=100000
1<=M<=100000
对于全部的数据,1<=z<=10^9
这道题挺经典的,做法是按DFS序重标号,然后:
一.加入一个点b,找到有宝物的集合中重标号在其左边最近的一个和右边最近的一个,左边若没有则找到右边最远的一个,右边没有则找到左边最远的一个,记为a和b,答案加上lca(a,b)+lca(b,c)-lca(a,b),不难发现这样是正确的.
二.删除一个点b,同样方法找到a,c,答案减去lca(a,b)+lca(b,c)-lca(a,b),类似。
还有集合的维护其实可以用STL::set,我用的BIT+二分,代码量大了一点……
1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 using namespace std; 5 const int maxn=100010; 6 long long ans; 7 long long dis[maxn][20]; 8 int cnt,fir[maxn],to[maxn<<1],nxt[maxn<<1],val[maxn<<1]; 9 int n,m,mm,fa[maxn][20],dep[maxn],bit[maxn]; 10 void addedge(int a,int b,long long d){ 11 nxt[++cnt]=fir[a];to[cnt]=b;val[cnt]=d;fir[a]=cnt; 12 } 13 void DFS(int node){ 14 for(int i=fir[node];i;i=nxt[i]){ 15 if(to[i]==fa[node][0])continue; 16 fa[to[i]][0]=node; 17 dis[to[i]][0]=val[i]; 18 dep[to[i]]=dep[node]+1; 19 DFS(to[i]); 20 } 21 } 22 23 void Init(){ 24 for(int k=1;k<=mm;k++) 25 for(int i=1;i<=n;i++) 26 fa[i][k]=fa[fa[i][k-1]][k-1], 27 dis[i][k]=dis[i][k-1]+dis[fa[i][k-1]][k-1]; 28 } 29 30 int ntp[maxn],ptn[maxn],cont; 31 32 void DFS2(int node){ 33 ntp[node]=++cont;ptn[cont]=node; 34 for(int i=fir[node];i;i=nxt[i]) 35 if(to[i]!=fa[node][0]) 36 DFS2(to[i]); 37 } 38 39 int Query(int x){ 40 int ret=0; 41 while(x){ 42 ret+=bit[x]; 43 x-=x&(-x); 44 } 45 return ret; 46 } 47 48 void add(int x,int d){ 49 while(x<=n){ 50 bit[x]+=d; 51 x+=x&(-x); 52 } 53 } 54 55 long long Lca(int x,int y){ 56 long long ret=0; 57 if(dep[x]<dep[y])swap(x,y); 58 for(int i=mm,d=dep[x]-dep[y];i>=0;i--) 59 if(d&(1<<i)) 60 ret+=dis[x][i],x=fa[x][i]; 61 62 for(int i=mm;x!=y;i?i--:i){ 63 if(!i||fa[x][i]!=fa[y][i]) 64 ret+=dis[x][i]+dis[y][i], 65 x=fa[x][i],y=fa[y][i]; 66 } 67 return ret; 68 } 69 70 int Ql(int l,int r){ 71 if(Query(r)-Query(l-1)==0)return r+1; 72 while(l<r){ 73 int mid=(l+r)>>1; 74 if(Query(mid)-Query(l-1))r=mid; 75 else l=mid+1; 76 } 77 return l; 78 } 79 80 int Qr(int l,int r){ 81 if(Query(r)-Query(l-1)==0)return l-1; 82 while(l<r){ 83 int mid=(l+r+1)>>1; 84 if(Query(r)-Query(mid-1))l=mid; 85 else r=mid-1; 86 } 87 return r; 88 } 89 90 int main(){ 91 scanf("%d%d",&n,&m); 92 while(1<<(mm+1)<=n)mm++; 93 for(int i=1;i<n;i++){ 94 int a,b,v; 95 scanf("%d%d%d",&a,&b,&v); 96 addedge(a,b,v); 97 addedge(b,a,v); 98 } 99 DFS(1);Init();DFS2(1); 100 101 int p,j,k; 102 while(m--){ 103 scanf("%d",&p); 104 if(Query(ntp[p])-Query(ntp[p]-1)){ 105 add(ntp[p],-1); 106 j=Qr(1,ntp[p]-1); 107 k=Ql(ntp[p]+1,n); 108 if(j==0&&k==n+1); 109 else{ 110 if(j==0) j=Qr(ntp[p]+1,n); 111 if(k==n+1) k=Ql(1,ntp[p]-1); 112 ans-=Lca(ptn[j],p)+Lca(ptn[k],p)-Lca(ptn[j],ptn[k]); 113 } 114 } 115 else{ 116 add(ntp[p],1); 117 j=Qr(1,ntp[p]-1); 118 k=Ql(ntp[p]+1,n); 119 if(j==0&&k==n+1); 120 else{ 121 if(j==0) j=Qr(ntp[p]+1,n); 122 if(k==n+1) k=Ql(1,ntp[p]-1); 123 ans+=Lca(ptn[j],p)+Lca(ptn[k],p)-Lca(ptn[j],ptn[k]); 124 } 125 } 126 printf("%lld\n",ans); 127 } 128 return 0; 129 }
尽最大的努力,做最好的自己!