P3320 [SDOI2015]尋寶遊戲(set+LCA)
阿新 • • 發佈:2022-04-01
題意
一棵 \(n\) 個節點的樹,邊有邊權。
每個點可能是關鍵點,每次操作改變一個點是否是關鍵點。
求所有關鍵點形成的極小連通子樹的邊權和的兩倍。
輸入格式
第一行,兩個整數 N、M,其中 M 為寶物的變動次數。
接下來的 N-1 行,每行三個整數 x、y、z,表示村莊 x、y 之間有一條長度為 z 的道路。
接下來的 M 行,每行一個整數 t,表示一個寶物變動的操作。若該操作前村莊 tt 內沒有寶物,則操作後村莊內有寶物;若該操作前村莊 t 內有寶物,則操作後村莊內沒有寶物。
輸出格式
M 行,每行一個整數,其中第 i 行的整數表示第 i 次操作之後玩家找到所有寶物需要行走的最短路程。若只有一個村莊內有寶物,或者所有村莊內都沒有寶物,則輸出 0.
資料範圍
\(1 \leq N \leq 100000\),
\(\ 1 \leq M \leq 100000\),
\(\ 1 \leq z \leq 10^9\)
樣例
input
4 5
1 2 30
2 3 50
2 4 60
2
3
4
2
1
output
0
100
220
220
280
思路
DFS序求出之後,關鍵點按DFS排序後是{\({a_1,a_2,...,a_k}\)}。
那麼所有關鍵點形成的極小聯通子樹的邊權和的兩倍等於\(dis(a_1,a_2)\)+\(dis(a_2,a_3)\)+...+\(dis(a_{k-1},a_k)\)+\(dis(a_k,a_1)\)。
那麼求一下 DFS 序,每次操作相當於往集合里加入/刪除一個元素。
假設插入 \(x\),它DFS序左右兩邊分別是 \(y\) 和 \(z\)。那麼答案加上 \(\mathrm{dis}(x,y)+\mathrm{dis}(x,z)-\mathrm{dis}(y,z)\) 即可。
刪除同理。
code
#include <bits/stdc++.h> using namespace std; typedef long long ll; typedef unsigned long long ull; //#pragma GCC optimize(3) #define pb push_back #define is insert #define PII pair<int,int> #define PLL pair<ll,ll> #define show(x) cerr<<#x<<" : "<<x<<endl; //mt19937 mt19937random(std::chrono::system_clock::now().time_since_epoch().count()); //ll getRandom(ll l,ll r){return uniform_int_distribution<ll>(l,r)(mt19937random);} const int INF=0x3f3f3f3f;//2147483647; const int N=1e5+50,M=1e5+50; const ll mod=998244353; int n,m; int head[N]; int tot=0; struct node { int to,nxt;ll val; }e[M<<1]; void add_edge(int u,int v,ll val){ e[tot].to=v,e[tot].nxt=head[u],e[tot].val=val,head[u]=tot++; } int rt; int depth[N]; int fa[N][18]; ll dis[N]; queue<int>q; int dfn[N],idf[N],idx=0; void dfs(int x,ll step,int pre){ dfn[x]=++idx;idf[idx]=x; dis[x]=step; for(int i=head[x];~i;i=e[i].nxt){ if(e[i].to==pre){ continue; } dfs(e[i].to,step+e[i].val,x); } } void init_bfs(){ for(int i=1;i<=n;i++){ depth[i]=INF; } depth[0]=0; depth[rt]=1; fa[rt][0]=0; q.push(rt); while(!q.empty()){ int u=q.front();q.pop(); for(int i=head[u];~i;i=e[i].nxt){ int v=e[i].to; if(depth[v]>depth[u]+1){ depth[v]=depth[u]+1; fa[v][0]=u; q.push(v); for(int k=1;k<=17;k++){ fa[v][k]=fa[fa[v][k-1]][k-1]; } } } } } int lca(int u,int v){ if(depth[u]<depth[v])swap(u,v); for(int k=17;k>=0;k--){ if(depth[fa[u][k]]>=depth[v]){ u=fa[u][k]; } } if(u==v){ return u; } for(int k=17;k>=0;k--){ if(fa[u][k]!=fa[v][k]){ u=fa[u][k]; v=fa[v][k]; } } return fa[u][0]; } set<int>s; int vis[N]; int cnt=0; ll getdis(int u,int v){ int tmp=lca(u,v); return dis[u]+dis[v]-dis[tmp]*2; } void solve() { memset(head,-1,sizeof head); cin>>n>>m; for(int i=1;i<=n-1;i++){ int u,v,val;cin>>u>>v>>val; add_edge(u,v,val); add_edge(v,u,val); } rt=1; dfs(rt,0,-1); init_bfs(); ll ans=0; // for(int i=1;i<=n;i++){ // cout<<dfn[i]<<" "; // }cout<<endl; for(int i=1;i<=m;i++){ int tmp;cin>>tmp; tmp=dfn[tmp]; if(!vis[tmp]) s.is(tmp); auto it = s.lower_bound(tmp); auto itt = s.upper_bound(tmp); int y, z; if (it == s.begin()) { y = (*--s.end()); } else { y = (*--it); } if (itt == s.end()) { z = (*s.begin()); } else { z = (*itt); } ll delta=getdis(idf[tmp], idf[y]) + getdis(idf[tmp], idf[z]) - getdis(idf[y], idf[z]); if(vis[tmp]){ s.erase(tmp); ans-=delta; vis[tmp]=0; } else { vis[tmp]=1; ans+=delta; } cout<<ans<<"\n"; } } signed main(){ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); int __=1;//cin>>__; while(__--){ solve(); } return 0; }