Gym101142G Gangsters in Central City
题目链接:https://cn.vjudge.net/problem/Gym-101142G
知识点: DFS序、LCA
题目大意:
给定一棵有根树(根为 \(1\))。每次修改叶子节点会被染成黑色(最开始都是白色的),也可以被染回白色。给出 \(q\) 次修改,对每次修改问最少需要切断几条边就能保证黑色叶子不在树上,和保证用最少的切断数使得黑色叶子不在树上的前提下最少会有多少白色叶子与根的联系也被切断了。
解题思路:
设根有 \(k\) 棵子树,除了根以外的所有结点都在这 \(k\) 棵子树中的一棵。
第一个问题很容易解答:对于要被染成黑色的叶子,如果这个叶子所在的子树中已有其他黑色叶子,则答案不变,否则答案加一;对于要被染回白色的叶子,如果叶子所在子树中还有其他黑色叶子,则答案不变,否则答案减一。
现在来解决第二个问题。其实,对于每一次修改,关键是如何找到这次修改的叶子所在子树剩下的所有黑色叶子的 \(LCA\)——用这个方法来解决这个难点:先预处理出树的 \(DFS\) 序,那么每次只要找到该子树中所有黑色叶子中 \(DFS\) 序最小和最大的两个点的 \(LCA\) 即可。(扩展到一般的情况:找树上的大于两个的结点的 \(LCA\),只需找这些结点中 \(DFS\) 序最小和最大的两个结点的 \(LCA\) 即可)。
AC代码:
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int MAXN=1e5+5,DEG=20; 4 5 vector<int> G[MAXN]; 6 int fa[MAXN][DEG]; 7 int deg[MAXN]; 8 9 int gfa[MAXN],have[MAXN]; 10 void dfs2(int rt,int grandfa){ 11 gfa[rt]=grandfa; 12 have[rt]=0; 13 for(int i=0;i<G[rt].size();i++){ 14 int v=G[rt][i]; 15 dfs2(v,grandfa); 16 have[rt]+=have[v]; 17 } 18 if(!have[rt]) have[rt]=1; 19 } 20 int tid=1,dfs_id[MAXN],rid[MAXN]; 21 void DFS(int rt){ 22 dfs_id[rt]=tid++; 23 for(int i=0;i<G[rt].size();i++){ 24 int v=G[rt][i]; 25 if(!dfs_id[v]) 26 DFS(v); 27 } 28 } 29 void BFS(int root){ 30 queue<int> que; 31 deg[root]=0; 32 fa[root][0]=root; 33 que.push(root); 34 while(!que.empty()){ 35 int tmp=que.front(); 36 que.pop(); 37 for(int i=1;i<DEG;i++) 38 fa[tmp][i]=fa[fa[tmp][i-1]][i-1]; 39 for(int i=0;i<G[tmp].size();i++){ 40 int v=G[tmp][i]; 41 if(v==fa[tmp][0]) continue; 42 deg[v]=deg[tmp]+1; 43 fa[v][0]=tmp; 44 que.push(v); 45 } 46 } 47 } 48 int LCA(int u,int v){ 49 if(deg[u]>deg[v]) swap(u,v); 50 int hu=deg[u],hv=deg[v]; 51 int tu=u,tv=v; 52 for(int det=hv-hu,i=0;det;det>>=1,i++){ 53 if(det&1) 54 tv=fa[tv][i]; 55 } 56 if(tu==tv) return tu; 57 for(int i=DEG-1;i>=0;i--){ 58 if(fa[tu][i]==fa[tv][i]) 59 continue; 60 tu=fa[tu][i]; 61 tv=fa[tv][i]; 62 } 63 return fa[tu][0]; 64 } 65 int used[MAXN]; 66 set<int> ids[MAXN]; 67 int last[MAXN]; 68 int main(){ 69 freopen("gangsters.in","r",stdin); 70 freopen("gangsters.out","w",stdout); 71 int n,q; 72 scanf("%d%d",&n,&q); 73 for(int i=2;i<=n;i++){ 74 int p; 75 scanf("%d",&p); 76 G[p].push_back(i); 77 } 78 BFS(1); 79 DFS(1); 80 for(int i=1;i<=n;i++) 81 rid[dfs_id[i]]=i; 82 for(int i=0;i<G[1].size();i++){ 83 dfs2(G[1][i],G[1][i]); 84 have[1]+=have[G[1][i]]; 85 } 86 87 char od[5]; 88 int nd; 89 int ans1=0,ans2=0; 90 while(q--){ 91 scanf("%s %d",od,&nd); 92 if(od[0]=='+'){ 93 used[gfa[nd]]++; 94 ids[gfa[nd]].insert(dfs_id[nd]); 95 if(used[gfa[nd]]==1){ 96 ans1++; 97 printf("%d %d\n",ans1,ans2); 98 continue; 99 } 100 } 101 else{ 102 used[gfa[nd]]--; 103 ids[gfa[nd]].erase(dfs_id[nd]); 104 if(!used[gfa[nd]]){ 105 ans1--; 106 printf("%d %d\n",ans1,ans2); 107 continue; 108 } 109 } 110 int l=*ids[gfa[nd]].begin(),r=*(--ids[gfa[nd]].end()); 111 int t=LCA(rid[l],rid[r]); 112 ans2-=last[gfa[nd]]; 113 last[gfa[nd]]=have[t]-used[gfa[nd]]; 114 ans2+=last[gfa[nd]]; 115 printf("%d %d\n",ans1,ans2); 116 } 117 118 return 0; 119 }
“这些年我一直提醒自己一件事情,千万不要自己感动自己。大部分人看似的努力,不过是愚蠢导致的。什么熬夜看书到天亮,连续几天只睡几小时,多久没放假了,如果这些东西也值得夸耀,那么富士康流水线上任何一个人都比你努力多了。人难免天生有自怜的情绪,唯有时刻保持清醒,才能看清真正的价值在哪里。”