[ZJOI2007]捉迷藏
这是一道动态点分治的练习题,学习之前先了解一下点分治。
引用某神犇的一句话:
树上的动态点分治就是序列上的线段树。
这句话非常精髓。
动态点分治是将我们普通点分治时维护的信息存储下来,只需要nlog的空间,并且用fa数组连接起来每一次点分治的根,即在点分治的过程中加上一句话
fa[rt]=f
这样便形成了一棵树状结构,也就是"点分树",树上的每一个点表示点分治过程中以他为根的那一块。
我们发现,对于原树中的每一个点,他最多属于点分树上的log个点。那么我们便可以对于每一个点分树上的点维护某种数据结构,实现单次修改log级别。那么最终复杂度就是log^2级别的。
这种东西听起来很简单,感受起来却很模糊,建议写一下看完代码后写一写这道题,大概就可以理解动态点分治是什么东西了。
首先我们考虑这道题的静态版本,其实就是"树的直径",两次dfs那个版本无法应用于这道题。还有树规版本,即对每一个点分别求出他所有儿子的子树中离他最远的点,取出最大与次大的那两个点作为经过这个根的最长路径即可。(当然还需要考虑根本身作为路径一端的情况)
着明显有一点点分治的感觉。
那么我直接说做法。
本题维护需要支持log时间的插入删除,求最值等操作,所以要用multiset,但是大多数人都选择模拟两个堆来实现multiset的功能,具体就是开两个堆,一个表示剩余元素,另外一个表示应该删除但没删除的元素,每次操作先检验顶部元素是否相等,这样实现的话删除插入依旧是均摊log级别的。我们将其封装为一个结构体。
维护三个这样的数据结构,A[inf],B[inf],C
A[i]表示当前点作为根时所拥有的那一部分子树中合法的点距离当前点在点分树上父亲的距离的集合。
B[i]表示当前点各个儿子中(当然是点分树上的儿子)的A集合中最大值的集合。(当然当前点如果合法,还需要插入0这个值)
C表示对于每一个点的B的最大值与次大值之和(即经过那个点的最长路径)的集合。
距离可以通过lca得到。那么以上都可以通过一边dfs求得。有一些小细节,好像不注意也是没有问题的,就是点分树的根是没有A数组的。
那么每次输出的时候只需要看一下C集合即可,对于那些不合法的情况,我们可以通过维护当前关灯房间的个数判掉。
那么对于修改的话,假如修改x。
考虑哪些集合受影响。
x及x点分树上所有祖先的A受影响,进而影响其父亲的B,进而影响到C。当然还会影响x本身的B。
那么如何修改呢?
看起来会很麻烦,尤其是对于集合大小判断的时候。我YY了好久想到一个异常舒服的办法。
修改当前位置的时候,我们可以消除当前情况下对其他集合的影响,即删除B[fa[x]]里面的A[x].top,前提是A[x].top存在。同理也删除B[fa[x]]对于C的贡献。然后修改当前点,具体就是插入或者删除某个距离。然后重新计算对B[fa[x]]及C的贡献。可以发现这样处理很方便而且绝对正确。
那么至此这道题就结束了。
附上代码:
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
1 #include<bits/stdc++.h> 2 using namespace std; 3 const int inf=1e5+10; 4 int n,now_cnt,q,tot,w[inf],fi[inf],nxt[inf<<1],to[inf<<1]; 5 void link(int x,int y){ 6 to[++tot]=y;nxt[tot]=fi[x];fi[x]=tot; 7 } 8 int top[inf],Fa[inf],siz[inf],son[inf],dep[inf]; 9 void dfs1(int x){ 10 siz[x]=1; 11 for(int i=fi[x];i;i=nxt[i]){ 12 if(to[i]==Fa[x])continue; 13 Fa[to[i]]=x; 14 dep[to[i]]=dep[x]+1; 15 dfs1(to[i]); 16 siz[x]+=siz[to[i]]; 17 if(siz[to[i]]>siz[son[x]])son[x]=to[i]; 18 } 19 } 20 void dfs2(int x,int y){ 21 top[x]=y; 22 if(son[x])dfs2(son[x],y); 23 for(int i=fi[x];i;i=nxt[i]){ 24 if(top[to[i]])continue; 25 dfs2(to[i],to[i]); 26 } 27 } 28 int get_dis(int x,int y){ 29 int tmp=dep[x]+dep[y]; 30 while(top[x]!=top[y]){ 31 if(dep[top[x]]<dep[top[y]])swap(x,y); 32 x=Fa[top[x]]; 33 } 34 return tmp-min(dep[x],dep[y])*2; 35 } 36 struct ghb_heap{ 37 priority_queue<int>INS,DEL; 38 void pop(){ 39 while(!DEL.empty() && INS.top()==DEL.top()) 40 INS.pop(),DEL.pop(); 41 INS.pop(); 42 } 43 int top(){ 44 while(!DEL.empty() && INS.top()==DEL.top()) 45 INS.pop(),DEL.pop(); 46 return INS.top(); 47 } 48 void insert(int x){ 49 INS.push(x); 50 } 51 void del(int x){ 52 DEL.push(x); 53 } 54 int size(){ 55 return INS.size()-DEL.size(); 56 } 57 int longest(){ 58 while(!DEL.empty() && INS.top()==DEL.top()) 59 INS.pop(),DEL.pop(); 60 int u=INS.top(); 61 INS.pop(); 62 while(!DEL.empty() && INS.top()==DEL.top()) 63 INS.pop(),DEL.pop(); 64 int v=INS.top(); 65 INS.push(u); 66 return u+v; 67 } 68 }A[inf],B[inf],C; 69 int vis[inf],fa[inf]; 70 int id,Min_size; 71 void getsize(int x,int f){ 72 siz[x]=1; 73 for(int i=fi[x];i;i=nxt[i]){ 74 if(vis[to[i]] || to[i]==f)continue; 75 getsize(to[i],x); 76 siz[x]+=siz[to[i]]; 77 } 78 } 79 void getrt(int x,int f,int y){ 80 int tmp=-0x3fffffff; 81 for(int i=fi[x];i;i=nxt[i]){ 82 if(vis[to[i]] || to[i]==f)continue; 83 getrt(to[i],x,y); 84 tmp=max(tmp,siz[to[i]]); 85 } 86 tmp=max(tmp,y-siz[x]); 87 if(tmp<Min_size){ 88 Min_size=tmp; 89 id=x; 90 } 91 } 92 void dfs(int x,int f,int y){ 93 A[y].insert(get_dis(x,fa[y])); 94 for(int i=fi[x];i;i=nxt[i]){ 95 if(vis[to[i]] || to[i]==f)continue; 96 dfs(to[i],x,y); 97 } 98 } 99 void work(int x){ 100 B[x].insert(0); 101 if(!fa[x])return ; 102 dfs(x,0,x); 103 B[fa[x]].insert(A[x].top()); 104 } 105 void Div(int x,int f){ 106 getsize(x,0); 107 Min_size=0x3fffffff; 108 getrt(x,0,siz[x]); 109 int rt=id; 110 vis[rt]=1; 111 fa[rt]=f; 112 work(rt); 113 for(int i=fi[rt];i;i=nxt[i]){ 114 if(vis[to[i]])continue; 115 Div(to[i],rt); 116 } 117 if(B[rt].size()>=2)C.insert(B[rt].longest()); 118 } 119 void init(){ 120 dfs1(1); 121 dfs2(1,1); 122 Div(1,0); 123 } 124 void turnon(int x){ 125 if(B[x].size()>=2)C.del(B[x].longest()); 126 B[x].del(0); 127 if(B[x].size()>=2)C.insert(B[x].longest()); 128 int now=x; 129 while(fa[now]){ 130 if(B[fa[now]].size()>=2)C.del(B[fa[now]].longest()); 131 B[fa[now]].del(A[now].top()); 132 A[now].del(get_dis(x,fa[now])); 133 if(A[now].size()>=1)B[fa[now]].insert(A[now].top()); 134 if(B[fa[now]].size()>=2)C.insert(B[fa[now]].longest()); 135 now=fa[now]; 136 } 137 } 138 void turnoff(int x){ 139 if(B[x].size()>=2)C.del(B[x].longest()); 140 B[x].insert(0); 141 if(B[x].size()>=2)C.insert(B[x].longest()); 142 int now=x; 143 while(fa[now]){ 144 if(B[fa[now]].size()>=2)C.del(B[fa[now]].longest()); 145 if(A[now].size()>=1)B[fa[now]].del(A[now].top()); 146 A[now].insert(get_dis(x,fa[now])); 147 B[fa[now]].insert(A[now].top()); 148 if(B[fa[now]].size()>=2)C.insert(B[fa[now]].longest()); 149 now=fa[now]; 150 } 151 } 152 int main() 153 { 154 freopen("hide.in","r",stdin); 155 freopen("hide.out","w",stdout); 156 scanf("%d",&n); 157 now_cnt=n; 158 for(int i=1;i<n;i++){ 159 int x,y; 160 scanf("%d%d",&x,&y); 161 link(x,y);link(y,x); 162 } 163 init(); 164 scanf("%d",&q); 165 for(int i=1;i<=q;i++){ 166 char s[10]; 167 int x; 168 scanf("%s",s); 169 if(s[0]=='C'){ 170 scanf("%d",&x); 171 if(w[x]){ 172 turnoff(x); 173 now_cnt++; 174 } 175 else { 176 turnon(x); 177 now_cnt--; 178 } 179 w[x]^=1; 180 } 181 else { 182 if(now_cnt==0)printf("-1\n"); 183 else if(now_cnt==1)printf("0\n"); 184 else printf("%d\n",C.top()); 185 } 186 } 187 return 0; 188 }