[ZJOI2007]捉迷藏

[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的贡献。可以发现这样处理很方便而且绝对正确。

那么至此这道题就结束了。

附上代码:

  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 }
View Code

 

转载于:https://www.cnblogs.com/hyghb/p/8477945.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值