CH#56C 異象石(LCA)(DFS序)
阿新 • • 發佈:2018-11-11
題目
Adera是Microsoft應用商店中的一款解謎遊戲。
異象石是進入Adera中異時空的引導物,在Adera的異時空中有一張地圖。這張地圖上有N個點,有N-1條雙向邊把它們連通起來。起初地圖上沒有任何異象石,在接下來的M個時刻中,每個時刻會發生以下三種類型的事件之一:
1. 地圖的某個點上出現了異象石(已經出現的不會再次出現);
2. 地圖某個點上的異象石被摧毀(不會摧毀沒有異象石的點);
3. 向玩家詢問使所有異象石所在的點連通的邊集的總長度最小是多少。
請你作為玩家回答這些問題。
題解
lca+dfs序
簡化題意後,這題就是一個動態求連線多點最小路徑長的問題。
對於這類問題,我們先跑一遍dfs,求出dfs序。對於一個新插入的點x,找到它dfs序相鄰的兩個出現異象石的節點,即為l和r。我們可以得到新增的路徑長為( -path(l,r)+path(l,x)+path(x,r) )/2。其實相當於在dfs序上處理這些問題,因為dfs序涉及入和出,所以才有除以2。再想,先除去path(l,r),然後加上(l,x)和(x,r),很好的表示了新的最小長度。刪除節點是大同小異,把這個點刪掉新增的路徑為( -(path(l,x)+path(x,r)) + path(l,r) )/2。
具體實現時,為了方便找到前驅和後繼,可以用set維護。
程式碼
#include<set> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; typedef long long ll; const int maxn=1e5+10; int bin[30]; int n,m; set<int> list; struct E{int y,c,next;}e[maxn*2];int len=0,last[maxn]; void ins(int x,int y,int c) { e[++len]=(E){y,c,last[x]};last[x]=len; } int id=0,dfn[maxn]; ll d[maxn];int dep[maxn],f[maxn][30]; void dfs(int x,int fa) { for(int k=last[x];k;k=e[k].next) { int y=e[k].y; if(y==fa) continue; dfn[y]=++id; dep[dfn[y]]=dep[dfn[x]]+1; d[dfn[y]]=d[dfn[x]]+e[k].c; f[dfn[y]][0]=dfn[x]; for(int i=1;i<=20;i++) f[dfn[y]][i]=f[f[dfn[y]][i-1]][i-1]; dfs(y,x);//debug } } int lca(int x,int y) { if(dep[x]<dep[y]) swap(x,y); for(int i=20;i>=0;i--) if(dep[x]-bin[i]>=dep[y]) x=f[x][i]; // if(dep[x]-bin[i]<=dep[y]) x=f[x][i];debug if(x==y) return x; for(int i=20;i>=0;i--) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i]; return f[x][0]; } ll path(int x,int y) { return d[x]+d[y]-2*d[lca(x,y)];//debug d[lca(x,y)] } char opt[4]; int main() { bin[0]=1;for(int i=1;i<=20;i++) bin[i]=bin[i-1]<<1; scanf("%d",&n); for(int i=1;i<n;i++) { int x,y,c; scanf("%d%d%d",&x,&y,&c); ins(x,y,c);ins(y,x,c); } dfn[1]=++id;dfs(1,0);//dfn[1]=1; scanf("%d",&m); ll ans=0; for(int i=1;i<=m;i++) { int x; scanf("%s",opt); if(opt[0]=='+') { scanf("%d",&x); if(list.size()==0) list.insert(dfn[x]); else if(list.size()==1) { ans=2*path(*list.begin(),dfn[x]); list.insert(dfn[x]); } else { list.insert(dfn[x]); set<int>::iterator il=list.find(dfn[x]),ir=il; if(ir==list.begin()) ir=list.end();ir--;//debug ir沒有-- if(++il==list.end()) il=list.begin(); ans=ans-path(*il,*ir)+path(*il,dfn[x])+path(dfn[x],*ir); } } else if(opt[0]=='-') { scanf("%d",&x); if(list.size()==1) list.clear(); else if(list.size()==2) { ans=0; list.erase(dfn[x]); } else { set<int>::iterator il=list.find(dfn[x]),ir=il; if(ir==list.begin()) ir=list.end();ir--;//debug ir沒有-- if(++il==list.end()) il=list.begin(); ans=ans+path(*il,*ir)-path(*il,dfn[x])-path(dfn[x],*ir); list.erase(dfn[x]); } } else { printf("%lld\n",ans/2); } } return 0; }