【题目描述】

Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子。某天,Jiajia、Wind和孩子们决定在家里玩捉迷藏游戏。他们的家很大且构造很奇特,由N个屋子和N-1条双向走廊组成,这N-1条走廊的分布使得任意两个屋子都互相可达。

游戏是这样进行的,孩子们负责躲藏,Jiajia负责找,而Wind负责操纵这N个屋子的灯。在起初的时候,所有的灯都没有被打开。每一次,孩子们只会躲 藏在没有开灯的房间中,但是为了增加刺激性,孩子们会要求打开某个房间的电灯或者关闭某个房间的电灯。为了评估某一次游戏的复杂性,Jiajia希望知道 可能的最远的两个孩子的距离(即最远的两个关灯房间的距离)。

我们将以如下形式定义每一种操作:

C(hange) i 改变第i个房间的照明状态,若原来打开,则关闭;若原来关闭,则打开。

G(ame)     开始一次游戏,查询最远的两个关灯房间的距离。

【输入格式】

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

【输出格式】

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

【样例输入】

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

【样例输出】

4

3

3

4

【提示】

对于20%的数据, N ≤50, M ≤100;

对于60%的数据, N ≤3000, M ≤10000;

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

  这道题有三种做法。

  我这里用线段树维护了一个括号序列。

 #include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
const int maxn=;
const int INF=;
int cnt,fir[maxn],nxt[maxn*],to[maxn*];
void addedge(int a,int b){
nxt[++cnt]=fir[a];
fir[a]=cnt;
to[cnt]=b;
}
int c[maxn];
int ID[maxn],rID[maxn*],tot;
struct Node{
int a,b,l1,l2,r1,r2,dis;
void Init(int p){
dis=-INF;a=b=;
if(rID[p]==-)b=;
if(rID[p]==-)a=;
if(rID[p]>&&c[rID[p]])
l1=l2=r1=r2=;
else
l1=l2=r1=r2=-INF;
}
void Push_up(Node l,Node r){
int a1=l.a,b1=l.b,a2=r.a,b2=r.b;
if(b1>=a2)a=a1,b=b1+b2-a2;
else a=a1+a2-b1,b=b2; dis=max(l.dis,r.dis);
dis=max(dis,max(l.r1+r.l2,l.r2+r.l1)); r1=max(r.r1,max(l.r1+b2-a2,l.r2+a2+b2));
r2=max(r.r2,l.r2+a2-b2); l1=max(l.l1,max(a1-b1+r.l1,a1+b1+r.l2));
l2=max(l.l2,r.l2+b1-a1);
}
}tr[(maxn*)<<]; void DFS(int x,int fa){
rID[++tot]=-;
rID[++tot]=x;
ID[x]=tot; for(int i=fir[x];i;i=nxt[i])
if(to[i]!=fa)
DFS(to[i],x);
rID[++tot]=-;
} void Build(int x,int l,int r){
if(l==r){
tr[x].Init(l); return;
}
int mid=(l+r)>>;
Build(x<<,l,mid);
Build(x<<|,mid+,r);
tr[x].Push_up(tr[x<<],tr[x<<|]);
} void Modify(int x,int l,int r,int g){
if(l==r){
tr[x].Init(l);
return;
}
int mid=(l+r)>>;
if(mid>=g)Modify(x<<,l,mid,g);
else Modify(x<<|,mid+,r,g);
tr[x].Push_up(tr[x<<],tr[x<<|]);
}
int n,Q,x;
char op[];
int main(){
#ifndef ONLINE_JUDGE
freopen("hide.in","r",stdin);
freopen("hide.out","w",stdout);
#endif
scanf("%d",&n);
for(int i=;i<=n;i++)c[i]=;
for(int i=,a,b;i<n;i++){
scanf("%d%d",&a,&b);
addedge(a,b);
addedge(b,a);
} DFS(,);
Build(,,tot); scanf("%d",&Q);
while(Q--){
scanf("%s",op);
if(op[]=='C'){
scanf("%d",&x);
(c[x])?n--:n++;c[x]^=;
Modify(,,tot,ID[x]);
}
else{
if(n==)printf("-1\n");
else if(n==)printf("0\n");
else printf("%d\n",tr[].dis);
}
}
return ;
}

  然后我又用点分治+堆的方法AC了一遍。

  先按点分治访问 rt 节点的顺序建 fa 边,每次改一个点只要延 fa 边一路改上去就可以了。 

  关于开三个堆:

  A:最终的答案。

  B:对于每个点,都有一个对应的B类堆,B中最多只有此节点的子节点个数的元素,B中元素为该点子节点的C堆的堆顶。

  C:对于每个点,都有一个对应的C类堆,堆中记录的是每一个子树中的节点对此点的 fa 的贡献。

 #include <iostream>
#include <cstring>
#include <cstdio>
#include <queue>
using namespace std;
const int maxn=; struct Heap{
priority_queue<int>A,B;
void Insert(int x){A.push(x);}
void Delete(int x){B.push(x);}
void Pop(){
while(B.size()&&A.top()==B.top())
A.pop(),B.pop();
A.pop();
}
int Size(){return A.size()-B.size();}
int Max(){
while(B.size()&&A.top()==B.top())
A.pop(),B.pop();
if(!A.size())return ;
return A.top();
}
int Max2(){
if(Size()<)return ;
int p=Max(),ret;Pop();ret=Max();
Insert(p);return ret;
}
}A,B[maxn],C[maxn]; int fir[maxn],nxt[maxn*],to[maxn*],cnt;
void addedge(int a,int b){nxt[++cnt]=fir[a];to[cnt]=b;fir[a]=cnt;} int tot,ID[maxn],dep[maxn];
int Min[maxn*][],mm[maxn*];
void DFS(int x,int fa){
Min[ID[x]=++tot][]=dep[x];
for(int i=fir[x];i;i=nxt[i])
if(to[i]!=fa){
dep[to[i]]=dep[x]+;
DFS(to[i],x);
Min[++tot][]=dep[x];
}
} int Dis(int x,int y){
if(ID[x]>ID[y])swap(x,y);
int k=mm[ID[y]-ID[x]+];
int ret=min(Min[ID[x]][k],Min[ID[y]-(<<k)+][k]);
return dep[x]+dep[y]-*ret;
} bool vis[maxn];
int sz[maxn],son[maxn],rt,N;
void Get_RT(int x,int fa){
sz[x]=;son[x]=;
for(int i=fir[x];i;i=nxt[i])
if(!vis[to[i]]&&to[i]!=fa){
Get_RT(to[i],x);
sz[x]+=sz[to[i]];
son[x]=max(son[x],sz[to[i]]);
}
son[x]=max(son[x],N-sz[x]);
if(!rt||son[rt]>son[x])rt=x;
} int fa[maxn];
void Div(int x,int f){
vis[x]=true;fa[x]=f;
for(int i=fir[x];i;i=nxt[i])
if(!vis[to[i]]){
rt=;N=sz[to[i]];
Get_RT(to[i],);
Div(rt,x);
}
} int light[maxn];
void Turn_OFF(int p,int x){
if(p==x){
B[x].Insert();
if(B[x].Size()==)
A.Insert(B[x].Max());
}
if(!fa[p])return;
int f=fa[p],mx=C[p].Max(),dis=Dis(f,x);
C[p].Insert(dis);
if(mx<dis){
int mt=B[f].Max()+B[f].Max2(),sz=B[f].Size();
if(C[p].Size()!=)
B[f].Delete(mx); B[f].Insert(dis);
if(mt<B[f].Max()+B[f].Max2()){
if(sz>=)A.Delete(mt);
if(B[f].Size()>=)A.Insert(B[f].Max()+B[f].Max2());
}
}
Turn_OFF(f,x);
} void Turn_ON(int p,int x){
if(p==x){
B[p].Delete();
if(B[p].Size()==)
A.Delete(B[p].Max());
}
if(!fa[p])return;
int f=fa[p],dis=Dis(f,x),mx;
C[p].Delete(dis);mx=C[p].Max();
if(mx<dis){
int mt=B[f].Max()+B[f].Max2(),sz=B[f].Size();
B[f].Delete(dis);
if(C[p].Size())
B[f].Insert(mx);
if(sz>=&&mt>B[f].Max()+B[f].Max2()){
if(sz>=)A.Delete(mt);
if(B[f].Size()>=)A.Insert(B[f].Max()+B[f].Max2());
}
}
Turn_ON(fa[p],x);
} int main(){
#ifndef ONLINE_JUDGE
freopen("hide.in","r",stdin);
freopen("hide.out","w",stdout);
#endif
int n;
scanf("%d",&n);
for(int i=,a,b;i<n;i++){
scanf("%d%d",&a,&b);
addedge(a,b);
addedge(b,a);
}
DFS(,);mm[]=-;
for(int i=;i<=tot;i++){
if((i&(i-))==)mm[i]=mm[i-]+;
else mm[i]=mm[i-];
}
for(int k=;k<=mm[tot];k++)
for(int i=;i+(<<k)-<=tot;i++)
Min[i][k]=min(Min[i][k-],Min[i+(<<(k-))][k-]);
N=n;rt=;
Get_RT(,);
Div(rt,); for(int i=;i<=n;i++)
Turn_OFF(i,i); char op[];
int Q,tot=n,x;
scanf("%d",&Q);
while(Q--){
scanf("%s",op);
if(op[]=='C'){
scanf("%d",&x);
if(light[x])Turn_OFF(x,x);
else Turn_ON(x,x);
light[x]^=;
}
else{
if(tot<)
printf("%d\n",tot-);
else
printf("%d\n",A.Max());
}
}
return ;
}

  

数据结构(括号序列,线段树||点分治,堆):ZJOI 2007 捉迷藏的更多相关文章

  1. bzoj 1095 [ZJOI2007]Hide 捉迷藏(括号序列+线段树)

    [题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=1095 [题意] 给定一棵树,树上颜色或白或黑而且可以更改,多个询问求最远黑点之间的距离 ...

  2. 【BZOJ】1095: [ZJOI2007]Hide 捉迷藏 括号序列+线段树

    [题目]BZOJ 1095 [题意]给定n个黑白点的树,初始全为黑点,Q次操作翻转一个点的颜色,或询问最远的两个黑点的距离,\(n \leq 10^5,Q \leq 5*10^5\). [算法]括号序 ...

  3. BZOJ1095 [ZJOI2007] Hide 捉迷藏 (括号序列 + 线段树)

    题意 给你一颗有 \(n\) 个点的树 , 共有 \(m\) 次操作 有两种类别qwq 将树上一个点染黑/白; 询问树上最远的两个黑点的距离. \((n \le 200000, m ≤500000)\ ...

  4. 【BZOJ 1095】 1095: [ZJOI2007]Hide 捉迷藏 (括号序列+线段树)

    1095: [ZJOI2007]Hide 捉迷藏 Description 捉迷藏 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子.某天,Jiajia.Wind和孩子们决定在家里玩捉迷藏游戏 ...

  5. 【BZOJ 2957】楼房重建&&Codechef COT5 Count on a Treap&&【NOIP模拟赛】Weed 线段树的分治维护

    线段树是一种作用于静态区间上的数据结构,可以高效查询连续区间和单点,类似于一种静态的分治.他最迷人的地方在于“lazy标记”,对于lazy标记一般随我们从父区间进入子区间而下传,最终给到叶子节点,但还 ...

  6. 【bzoj5210】最大连通子块和 树链剖分+线段树+可删除堆维护树形动态dp

    题目描述 给出一棵n个点.以1为根的有根树,点有点权.要求支持如下两种操作: M x y:将点x的点权改为y: Q x:求以x为根的子树的最大连通子块和. 其中,一棵子树的最大连通子块和指的是:该子树 ...

  7. 【UOJ228】基础数据结构练习题(线段树)

    [UOJ228]基础数据结构练习题(线段树) 题面 UOJ 题解 我们来看看怎么开根? 如果区间所有值都相等怎么办? 显然可以直接开根 如果\(max-sqrt(max)=min-sqrt(min)\ ...

  8. Snacks HDU 5692 dfs序列+线段树

    Snacks HDU 5692 dfs序列+线段树 题意 百度科技园内有n个零食机,零食机之间通过n−1条路相互连通.每个零食机都有一个值v,表示为小度熊提供零食的价值. 由于零食被频繁的消耗和补充, ...

  9. BZOJ 4025: 二分图 [线段树CDQ分治 并查集]

    4025: 二分图 题意:加入边,删除边,查询当前图是否为二分图 本来想练lct,然后发现了线段树分治的做法,感觉好厉害. lct做法的核心就是维护删除时间的最大生成树 首先口胡一个分块做法,和hno ...

随机推荐

  1. Codeforces 552E - Vanya and Brackets【表达式求值】

    给一个只有加号和乘号的表达式,要求添加一对括号使得最后结果最大.表达式长度5000,乘号最多12个,表达式中数字只有1位. 左括号一定在乘号右边,右括号一定在乘号左边,因为如果不是这样的话,一定可以调 ...

  2. JS实现一键复制功能

    var copyClick = function (d) { var Url2 = $(d).parent().parent().find("#copy_value"); Url2 ...

  3. CakePHP之控制器

    控制器 控制器是MVC中的“C”. 如果你的网站使用Cake框架制作,一般根据url地址和通过路由,就会找到正确的控制器,然后控制器的动作就会被调用. 一个控制器需要解释请求数据.确保使用正确的模型. ...

  4. HTML5 WebAudioAPI-实例(二)

    简单播放实例1: var url='../content/audio/海阔天空.mp3'; if (!window.AudioContext) { alert('您的浏览器不支持AudioContex ...

  5. (转)asp.net分页存储过程

    Asp.Net分页存储过程 SQL分页语句 一.比较万能的分页: sql代码: 1 2 3 select top 每页显示的记录数 * from topic where id not in  (sel ...

  6. Dictionary的遍历和修改

    /// <summary>        /// 初始化一个Dic        /// </summary>        public static void mainTe ...

  7. 显示scrollbar

    修改CSS overflow的值 overflow: 参考MDN 例子: overflow: auto or scroll

  8. 复制构造函数2——深入理解

    //如果不显示定义复制构造函数,编译会出错,原因是:在创建对象s2时,调用默认复制构造函数并用对象s1对其进行初始化,致使s2中指针 //与s1中指针指向同一储存空间,当一个对象生命周期结束后调用析构 ...

  9. 【cogs858】磁性链

    [题目描述] 有N块编号为1~N的特殊磁石相互吸附组成一条磁性链,只有它们紧挨着时才会传递吸力,他们之间的吸力很大,如果我们要从N块相连的磁石中取出一块,那么需要消耗N-1个单位的能量,空缺处不再有吸 ...

  10. 如何让低版本的IE浏览器(IE6/IE7/IE8)支持HTML5 header等新标签

    html5提供的一些新标签(article,aside,dialog,footer,header,section,footer,nav,figure,menu)使用起来非常的方便,但是低版本的IE浏览 ...