[bzoj1095][ZJOI2007]Hide 捉迷藏 点分树,动态点分治

【bzoj1095】[ZJOI2007]Hide 捉迷藏

2015年4月20日7,8876

Description

捉迷藏 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子。某天,Jiajia、Wind和孩子们决定在家里玩捉迷藏游戏。他们的家很大且构造很奇特,由N个屋子和N-1条双向走廊组成,这N-1条走廊的分布使得任意两个屋子都互相可达。游戏是这样进行的,孩子们负责躲藏,Jiajia负责找,而Wind负责操纵这N个屋子的灯。在起初的时候,所有的灯都没有被打开。每一次,孩子们只会躲藏在没有开灯的房间中,但是为了增加刺激性,孩子们会要求打开某个房间的电灯或者关闭某个房间的电灯。为了评估某一次游戏的复杂性,Jiajia希望知道可能的最远的两个孩子的距离(即最远的两个关灯房间的距离)。 我们将以如下形式定义每一种操作: C(hange) i 改变第i个房间的照明状态,若原来打开,则关闭;若原来关闭,则打开。 G(ame) 开始一次游戏,查询最远的两个关灯房间的距离。

Input

第一行包含一个整数N,表示房间的个数,房间将被编号为1,2,3…N的整数。接下来N-1行每行两个整数a, b,表示房间a与房间b之间有一条走廊相连。接下来一行包含一个整数Q,表示操作次数。接着Q行,每行一个操作,如上文所示。

Output

对于每一个操作Game,输出一个非负整数到hide.out,表示最远的两个关灯房间的距离。若只有一个房间是关着灯的,输出0;若所有房间的灯都开着,输出-1。

Sample Input

8
1 2
2 3
3 4
3 5
3 6
6 7
6 8
7
G
C 1
G
C 2
G
C 1
G

Sample Output

4
3
3
4

HINT

对于100%的数据, N ≤100000, M ≤500000。

C.每个重心存所有子树到其距离

B.每个重心存各个子树最大值,即子结点堆C的最大值

A.全局一个堆,维护答案最大值,存每个堆B的最大值和次大值之和

C的意思是u这个节点,经过u,到达其父亲f的路径的所有距离,

B是维护所有的子节点的C的最大值,

A是维护各个B中的最大值。

动态点分治是树上动态添加节点,在这种情况下动态维护点分树的结构

这道题目的树的结构是不变的,所以建出点分树即可,

然后维护各个堆的值即可。

因为点分树上是log层的,堆维护也是log的,所有每次询问logn

修改logn logn,所有总复杂度是n log^2n

计算深度那里十分巧妙,用一个rmq记录,就是通过dfs访问顺序,然后进入一个点记录一次,

这样总共均摊是2*n,和边有关系,

然后rmq记录最小值,发现这个就是lca的深度

然后距离就比较好算了,O(1)算出,

  1 #include<cstring>
  2 #include<cmath>
  3 #include<iostream>
  4 #include<cstdio>
  5 #include<algorithm>
  6 #include<queue>
  7
  8 #define inf 1000000007
  9 #define N 200007
 10 #define ll long long
 11 using namespace std;
 12 inline int read()
 13 {
 14     int x=0,f=1;char ch=getchar();
 15     while(ch<‘0‘||ch>‘9‘){if (ch==‘-‘)f=-1;ch=getchar();}
 16     while(ch>=‘0‘&&ch<=‘9‘){x=(x<<3)+(x<<1)+ch-‘0‘;ch=getchar();}
 17     return x*f;
 18 }
 19
 20 int n,m,rt,dfn,sum,tot;
 21 int bin[22],Log[N];
 22 int siz[N],f[N],dep[N];
 23 int cnt,hed[N],rea[N],nxt[N];
 24 int mn[19][N],pos[N],fa[N];
 25 bool vis[N],clo[N];
 26 struct heap
 27 {
 28     priority_queue<int> A,B;
 29     void push(int x){A.push(x);}
 30     void erase(int x){B.push(x);}
 31     void pop()
 32     {
 33         while(B.size()&&A.top()==B.top())
 34             A.pop(),B.pop();
 35         A.pop();
 36     }
 37     int top()
 38     {
 39         while(B.size()&&A.top()==B.top())
 40             A.pop(),B.pop();
 41         if(!A.size())return 0;
 42         return A.top();
 43     }
 44     int size(){return A.size()-B.size();}
 45     int stop()
 46     {
 47         if(size()<2)return 0;
 48         int x=top();pop();
 49         int y=top();push(x);
 50         return y;
 51     }
 52 }A,B[100005],C[100005];
 53
 54 void add(int u,int v)
 55 {
 56     nxt[++cnt]=hed[u];
 57     hed[u]=cnt;
 58     rea[cnt]=v;
 59 }
 60 void dfs(int u,int fa)
 61 {
 62     mn[0][++dfn]=dep[u],pos[u]=dfn;
 63     for (int i=hed[u];i!=-1;i=nxt[i])
 64     {
 65         int v=rea[i];
 66         if (v==fa)continue;
 67         dep[v]=dep[u]+1;
 68         dfs(v,u);
 69         mn[0][++dfn]=dep[u];
 70     }
 71 }
 72 void get_root(int u,int fa)
 73 {
 74     siz[u]=1,f[u]=0;
 75     for (int i=hed[u];i!=-1;i=nxt[i])
 76     {
 77         int v=rea[i];
 78         if (v==fa||vis[v])continue;
 79         get_root(v,u);
 80         siz[u]+=siz[v];
 81         f[u]=max(f[u],siz[v]);
 82     }
 83     f[u]=max(f[u],sum-siz[u]);
 84     if (f[u]<f[rt])rt=u;
 85 }
 86 void divide(int u,int par)
 87 {
 88     fa[u]=par,vis[u]=1;int sums=sum;
 89     for (int i=hed[u];i!=-1;i=nxt[i])
 90     {
 91         int v=rea[i];
 92         if (vis[v])continue;
 93         if (siz[v]>siz[u]) sum=sums-siz[u];
 94         else sum=siz[v];
 95         rt=0;
 96         get_root(v,u);
 97         divide(rt,u);
 98     }
 99 }
100 int rmq(int x,int y)
101 {
102     x=pos[x],y=pos[y];
103     if (y<x)swap(x,y);
104     int t=Log[y-x+1];
105     return min(mn[t][x],mn[t][y-bin[t]+1]);
106 }
107 int dis(int x,int y)
108 {
109     return dep[x]+dep[y]-2*rmq(x,y);
110 }
111 void turn_off(int u,int v)
112 {
113     if (u==v)
114     {
115         B[u].push(0);
116         if (B[u].size()==2)A.push(B[u].top());
117     }
118     if(!fa[u])return;
119     int f=fa[u],D=dis(f,v),tmp=C[u].top();
120     C[u].push(D);
121     if (D>tmp)
122     {
123         int mx=B[f].top()+B[f].stop(),size=B[f].size();
124         if (tmp)B[f].erase(tmp);
125         B[f].push(D);
126         int now=B[f].top()+B[f].stop();
127         if(now>mx)
128         {
129             if (size>=2)A.erase(mx);
130             if (B[f].size()>=2)A.push(now);
131         }
132     }
133     turn_off(f,v);
134 }
135 void turn_on(int u,int v)
136 {
137     if(u==v)
138     {
139         if(B[u].size()==2)A.erase(B[u].top());
140         B[u].erase(0);
141     }
142     if(!fa[u])return;
143     int f=fa[u],D=dis(f,v),tmp=C[u].top();
144     C[u].erase(D);
145     if(D==tmp)
146     {
147         int mx=B[f].top()+B[f].stop(),size=B[f].size();
148         B[f].erase(D);
149         if(C[u].top())B[f].push(C[u].top());
150         int now=B[f].top()+B[f].stop();
151         if(now<mx)
152         {
153             if(size>=2)A.erase(mx);
154             if(B[f].size()>=2)A.push(now);
155         }
156     }
157     turn_on(f,v);
158 }
159 int main()
160 {
161     bin[0]=1;for (int i=1;i<20;i++)bin[i]=bin[i-1]<<1;//2的幂次
162     Log[0]=-1;for (int i=1;i<=200000;i++)Log[i]=Log[i>>1]+1;
163     n=read();memset(hed,-1,sizeof(hed));
164     for(int i=1;i<n;i++)
165     {
166         int u=read(),v=read();
167         add(u,v),add(v,u);
168     }
169
170     dfs(1,0);//预处理出深度
171
172     for (int i=1;i<=Log[dfn];i++)
173         for (int j=1;j<=dfn;j++)
174             if (j+bin[i]-1<=dfn) mn[i][j]=min(mn[i-1][j],mn[i-1][j+bin[i-1]]);
175     //预处理,处理出向下走2^x步的到达最浅的点,就是起lca的深度。
176
177     rt=0,f[0]=inf,sum=n;
178     get_root(1,0),divide(rt,0);
179     for (int i=1;i<=n;i++)
180         clo[i]=true;
181     for (int i=1;i<=n;i++)turn_off(i,i);
182     tot=n;//tot记录当前多少灯是灭的。
183 //    printf("%d\n",A.top());
184
185     char ch[2];m=read();
186     while(m--)
187     {
188         scanf("%s",ch+1);
189         if (ch[1]==‘G‘)
190         {
191             if (tot<=1) printf("%d\n",tot-1);
192             else printf("%d\n",A.top());
193         }
194         else
195         {
196             int x=read();
197             if (clo[x])turn_on(x,x),tot--;
198             else turn_off(x,x),tot++;
199             clo[x]^=1;
200         }
201     }
202 }

原文地址:https://www.cnblogs.com/fengzhiyuan/p/8250931.html

时间: 2024-10-28 07:23:02

[bzoj1095][ZJOI2007]Hide 捉迷藏 点分树,动态点分治的相关文章

bzoj1095: [ZJOI2007]Hide 捉迷藏 动态点分治学习

好迷啊...感觉动态点分治就是个玄学,蜜汁把树的深度缩到logn (静态)点分治大概是递归的时候分类讨论: 1.答案经过当前点,暴力(雾)算 2.答案不经过当前点,继续递归 由于原树可以长的奇形怪状(菊花啊..链啊..扫把啊..)这就导致各种方法都会被卡 于是通过每次找重心保证最大深度 动态怎么解决呢? 不妨考虑线段树是二分的固态版本(只可意会),那么我们把每次找到的重心固定下来长成一棵树就可以把点分治凝固(不可言传) 原来点分治该维护什么现在就维护什么... (事实上我并没有写过静态点分治..

[BZOJ1095][ZJOI2007]Hide 捉迷藏

试题描述 捉迷藏 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子.某天,Jiajia.Wind和孩子们决定在家里玩捉迷藏游戏.他们的家很大且构造很奇特,由N个屋子和N-1条双向走廊组成,这N-1条走廊的分布使得任意两个屋子都互相可达.游戏是这样进行的,孩子们负责躲藏,Jiajia负责找,而Wind负责操纵这N个屋子的灯.在起初的时候,所有的灯都没有被打开.每一次,孩子们只会躲藏在没有开灯的房间中,但是为了增加刺激性,孩子们会要求打开某个房间的电灯或者关闭某个房间的电灯.为了评估某一次

bzoj千题计划245:bzoj1095: [ZJOI2007]Hide 捉迷藏

http://www.lydsy.com/JudgeOnline/problem.php?id=1095 查询最远点对,带修改 显然可以用动态点分治 对于每个点,维护两个堆 堆q1[x] 维护 点分树x的子树中,所有黑点到x的点分树中父节点的距离 堆q2[x]维护 点分树x的子节点的堆q1的堆顶,即若y是x在点分树中的子节点,则q2[x].push(q1[y].top()) 再来维护一个全局的堆Q,维护所有q2的堆顶,即Q.push(q2[x].top()) #include<cmath> #

bzoj千题计划252:bzoj1095: [ZJOI2007]Hide 捉迷藏

http://www.lydsy.com/JudgeOnline/problem.php?id=1095 点分树+堆 请去看 http://www.cnblogs.com/TheRoadToTheGold/p/8463436.html 线段树维护括号序列 对树进行dfs,入栈时加一个左括号,出栈时加一个右括号,那么书上两点间的距离=括号序列两点间不匹配括号数 例: 树1--2--3,2为根 括号序列为 (2(3)(1)) 2和1的距离 为 ()( = 1, 3和1的距离为 )( =2 具体怎么维

【BZOJ1095】[ZJOI2007]Hide 捉迷藏 动态树分治+堆

[BZOJ1095][ZJOI2007]Hide 捉迷藏 Description 捉迷藏 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子.某天,Jiajia.Wind和孩子们决定在家里玩捉迷藏游戏.他们的家很大且构造很奇特,由N个屋子和N-1条双向走廊组成,这N-1条走廊的分布使得任意两个屋子都互相可达.游戏是这样进行的,孩子们负责躲藏,Jiajia负责找,而Wind负责操纵这N个屋子的灯.在起初的时候,所有的灯都没有被打开.每一次,孩子们只会躲藏在没有开灯的房间中,但是为了增加刺激

【BZOJ 1095】 [ZJOI2007]Hide 捉迷藏

1095: [ZJOI2007]Hide 捉迷藏 Time Limit: 40 Sec  Memory Limit: 162 MB Submit: 1232  Solved: 501 [Submit][Status] Description 捉迷藏 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子.某天,Jiajia.Wind和孩子们决定在家里玩捉迷藏游戏.他们的家很大且构造很奇特,由N个屋子和N-1条双向走廊组成,这N-1条走廊的分布使得任意两个屋子都互相可达.游戏是这样进行的,孩子

bzoj 1095 [ZJOI2007]Hide 捉迷藏 动态点分治+堆

题面 题目传送门 解法 挺恶心的题 考虑动态点分治,先建出点分树 然后每一个点开两个堆,分别为\(a,b\) \(a_i\)表示点分树上\(i\)子树中所有节点在原树上和点分树中\(i\)父亲的距离,\(b_i\)表示点分树中\(i\)所有儿子的堆顶 再开一个堆\(ans\),存每一个\(b_i\)最大和次大值的和 在修改的时候,分两种情况考虑 但是本质都是一样的,就是在点分树上不断爬,爬到根为止,然后对当前点和父亲的\(a,b\)进行删除和加入 细节比较多,需要注意 重点:传入一个结构体参数的

bzoj 1095: [ZJOI2007]Hide 捉迷藏

Description 捉迷藏 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子.某天,Jiajia.Wind和孩子们决定在家里玩 捉迷藏游戏.他们的家很大且构造很奇特,由N个屋子和N-1条双向走廊组成,这N-1条走廊的分布使得任意两个屋 子都互相可达.游戏是这样进行的,孩子们负责躲藏,Jiajia负责找,而Wind负责操纵这N个屋子的灯.在起初的 时候,所有的灯都没有被打开.每一次,孩子们只会躲藏在没有开灯的房间中,但是为了增加刺激性,孩子们会要 求打开某个房间的电灯或者关闭某个房间

BZOJ 1095 ZJOI2007 Hide 捉迷藏 动态树分治+堆

题目大意:给定一棵树,一开始每个点都是黑点,多次改变某个点的状态或询问距离最远的两个黑点的距离 <珍爱生命远离STL可是我还是可耻地用了STL系列> 传说中的动态树分治...其实并没有那么神嘛= = ↑别听这傻瓜瞎说这货被STL卡了一天QAQ 我们把分治过程中遍历过的重心都连起来 上一层的重心链接下一层的重心 可以得到一棵新的树 下面我们开始讨论这棵新树 显然这棵树的高度不会超过O(logn) 然后我们每个节点开两个堆 第一个堆记录子树中所有节点到父亲节点的距离 第二个堆记录所有子节点的堆顶