【BZOJ3991】[SDOI2015]尋寶遊戲 樹鏈的並+set
阿新 • • 發佈:2017-06-24
自己 成了 沒有 很好 amp oid void out next
1 2 30
2 3 50
2 4 60
2
3
4
2
1
100
220
220
280
【BZOJ3991】[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 51 2 30
2 3 50
2 4 60
2
3
4
1
Sample Output
0100
220
220
280
HINT
1<=N<=100000
1<=M<=100000 對於全部的數據,1<=z<=10^9題解:從前有一個神奇的序列,它叫DFS序,它有一個神奇的性質,就是兩點間LCA的深度=兩點在DFS序上的區間中深度的最小值。從前有一堆樹鏈,它們跑到了DFS序上,並按DFS序排成了一列,它們的並就是每個樹鏈的長度-相鄰兩個樹鏈的LCA到根的路徑的長度。
這個性質其實很好理解,也很好證吧~
所以我們用set維護DFS序,每加入一個點就找出它在DFS序上的前驅後繼,計算樹鏈的並的變化長度,刪除時類似。不過由於可以從任意一個節點出發,所以總長度應該減去所有點的LCA到根的路徑長度(也就是DFS序最小的和最大的點的LCA),答案就是總長度*2
#include <cstdio> #include <cstring> #include <iostream> #include <set> using namespace std; const int maxn=100010; typedef long long ll; ll sum; int n,m,lgn,cnt,tot; int to[maxn<<1],next[maxn<<1],head[maxn],fa[maxn][20],p[maxn],q[maxn],dep[maxn],ins[maxn]; set<int> s; set<int>::iterator it; ll val[maxn<<1],len[maxn]; int rd() { int ret=0,f=1; char gc=getchar(); while(gc<‘0‘||gc>‘9‘) {if(gc==‘-‘)f=-f; gc=getchar();} while(gc>=‘0‘&&gc<=‘9‘) ret=ret*10+gc-‘0‘,gc=getchar(); return ret*f; } void add(int a,int b,int c) { to[cnt]=b,val[cnt]=c,next[cnt]=head[a],head[a]=cnt++; } void dfs(int x) { q[++p[0]]=x,p[x]=p[0]; for(int i=head[x];i!=-1;i=next[i]) if(to[i]!=fa[x][0]) fa[to[i]][0]=x,len[to[i]]=len[x]+val[i],dep[to[i]]=dep[x]+1,dfs(to[i]); } int lca(int a,int b) { if(dep[a]<dep[b]) swap(a,b); int i; for(i=lgn;i>=0;i--) if(dep[fa[a][i]]>=dep[b]) a=fa[a][i]; if(a==b) return a; for(i=lgn;i>=0;i--) if(fa[a][i]!=fa[b][i]) a=fa[a][i],b=fa[b][i]; return fa[a][0]; } int main() { n=rd(),m=rd(); int i,j,a,b,c; memset(head,-1,sizeof(head)); for(i=1;i<n;i++) a=rd(),b=rd(),c=rd(),add(a,b,c),add(b,a,c); dep[1]=1,dfs(1); for(j=1;(1<<j)<=n;j++) for(lgn=j,i=1;i<=n;i++) fa[i][j]=fa[fa[i][j-1]][j-1]; for(i=1;i<=m;i++) { a=rd(); if(!ins[a]) { it=s.upper_bound(p[a]),b=c=0; if(it!=s.end()) c=q[*it]; if(it!=s.begin()) it--,b=q[*it]; if(b&&c) sum+=len[lca(b,c)]; if(b) sum-=len[lca(a,b)]; if(c) sum-=len[lca(a,c)]; tot++,ins[a]=1,sum+=len[a],s.insert(p[a]); } else { s.erase(p[a]),it=s.upper_bound(p[a]),b=c=0; if(it!=s.end()) c=q[*it]; if(it!=s.begin()) it--,b=q[*it]; if(b&&c) sum-=len[lca(b,c)]; if(b) sum+=len[lca(a,b)]; if(c) sum+=len[lca(a,c)]; tot--,ins[a]=0,sum-=len[a]; } if(tot==1||tot==0) { printf("0\n"); continue; } it=s.begin(),b=q[*it],it=s.end(),--it,c=q[*it]; printf("%lld\n",2*(sum-len[lca(b,c)])); } return 0; }
【BZOJ3991】[SDOI2015]尋寶遊戲 樹鏈的並+set