zoukankan      html  css  js  c++  java
  • BZOJ 1095: [ZJOI2007]Hide 捉迷藏

    1095: [ZJOI2007]Hide 捉迷藏

    Time Limit: 40 Sec  Memory Limit: 256 MB
    Submit: 3585  Solved: 1480
    [Submit][Status][Discuss]

    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。

    Source

    分析:

    简化一下就是询问树上最远的两个黑点的距离,会存在修改操作...

    貌似有两种做法...

    一种叫做动态点分治...貌似理解起来比较容易...然而代码...

    一种叫做括号序列...跑得飞起...

    动态点分治:

    点分治的过程是选取重心来搞一搞...所以我们找到所有的重心,然后把这棵树建出来,树高是$log$的,然后我们在每个点上维护两个堆,第一个堆维护的是当前点的子树中所有黑点到当前点根节点的距离,第二个堆维护的是当前点所有子节点的堆顶,那么过当前点的最远点对就是第二堆的第一大和第二大的值之和...所以我们在全局维护一个堆,维护的是所有节点的最优值,也就是第二个堆的第一大和第二大值之和...

    考虑修改操作,修改一个点只会影响一条链,所以就暴力修改就好...

    括号序列:

    先去膜拜了岛娘的题解...

    考虑一棵树的深度优先遍历用括号序列表示出来,样例大概长介个样子~~~

    [ 1 [ 2 [ 3 [ 6 [ 8 ] [ 7 ] ] [ 5 ] [ 4 ] ] ] ]

    挺好看的???

    对于两个点之间的距离,就是他们之间的括号序列去掉完全匹配括号之后的括号数量,举个栗子(lizi...),我们需要求出$dis(3,5)$,取出两个节点之间的序列:[ [ ] [ ] ] [,去掉完全匹配的括号之后的序列就是 [ ,所以距离为$1$,感觉还是很显然的...

    我们已经知道两个点之间的距离怎么求出了,考虑如何维护黑点距离最大值,并且维护每个点的颜色,大概可以用线段树来维护...

    我们定义 ] 的数量为$a$,[ 的数量为$b$,那么如果我们要维护两个区间$(l,r)$的合并,大概就是酱紫:

    if(x.b>y.a) ans.a=x.a,ans.b=y.b+x.b-y.a;
    else        ans.a=x.a+y.a-x.b,ans.b=y.b;
    

     然后考虑如何维护区间最大值$dis$

    考虑$dis$只可能是$max(dis(l),dis(r),a(l)-b(l)+a(r)+b(r),a(l)+b(l)-a(r)+b(r))$

    所以我们对于每个区间维护四个值:

    $lp$代表$max(a+b)$前缀

    $lm$代表$max(b-a)$前缀

    $rp$代表$max(a+b)$后缀

    $rm$代表$max(a-b)$后缀

    那么更新的时候就是:

    ans.dis=max(max(x.dis,y.dis),max(x.rp+y.lm,x.rm+y.lp));
    

    四个值的更新如下:

    ans.lp =max(x.lp,max(y.lp-x.b+x.a,y.lm+x.b+x.a));
    ans.lm =max(x.lm,y.lm+x.b-x.a);
    ans.rp =max(y.rp,max(x.rp-y.a+y.b,x.rm+y.b+y.a));
    ans.rm =max(y.rm,x.rm+y.a-y.b);
    

     至于线段树的叶子节点,如果这个叶子节点是白点或者括号初始值就设为$-inf$,黑点则设为$0$...

    代码:

    动态点分治:

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<queue>
    //by NeighThorn
    using namespace std;
    
    const int maxn=100000+5;
    
    struct Heap{
    	
    	priority_queue<int> q1,q2;
    	
    	inline void Insert(int x){
    		q1.push(x);
    	}
    	
    	inline void Erase(int x){
    		q2.push(x);
    	}
    	
    	inline void Pop(void){
    		while(q2.size()&&q1.top()==q2.top())
    			q1.pop(),q2.pop();
    		q1.pop();
    	}
    	
    	inline int Top(void){
    		while(q2.size()&&q1.top()==q2.top())
    			q1.pop(),q2.pop();
    		return q1.top();
    	}
    	
    	inline int NextTop(void){
    		int tmp=Top();Pop();
    		int ans=Top();Insert(tmp);
    		return ans;
    	}
    	
    	inline int size(void){
    		return q1.size()-q2.size();
    	}
    	
    }pq[2][maxn],ans;
    
    int n,q,cnt,tot,co[maxn],hd[maxn],to[maxn<<1],nxt[maxn<<1],ban[maxn<<1];
    int f[maxn][25],fa[maxn],dep[maxn];
    
    char opt[3];
    
    inline void add(int x,int y){
    	to[cnt]=y;nxt[cnt]=hd[x];hd[x]=cnt++;
    }
    
    inline int DfsSize(int x,int FA){
    	int res=1;
    	for(int i=hd[x];i!=-1;i=nxt[i])
    		if(to[i]!=FA&&!ban[i])
    			res+=DfsSize(to[i],x);
    	return res;
    }
    
    inline void Insert(Heap &s){
    	if(s.size()>=2)
    		ans.Insert(s.Top()+s.NextTop());
    }
    
    inline void Erase(Heap &s){
    	if(s.size()>=2)
    		ans.Erase(s.Top()+s.NextTop());
    }
    
    inline int Find(int x,int FA,int siz,int &G){
    	int S=1,flag=1;
    	for(int i=hd[x];i!=-1;i=nxt[i])
    		if(to[i]!=FA&&!ban[i]){
    			int tmp=Find(to[i],x,siz,G);
    			if(tmp>siz/2) flag=0;
    			S+=tmp;
    		}
    	if(siz-S>siz/2) flag=0;
    	if(flag) G=x;return S;
    }
    
    inline void dfs(int x,int FA,int d,Heap &s){
    	s.Insert(d);
    	for(int i=hd[x];i!=-1;i=nxt[i])
    		if(to[i]!=FA&&!ban[i])
    			dfs(to[i],x,d+1,s);
    }
    
    inline int divide(int x){
    	int G,siz=DfsSize(x,-1);Find(x,-1,siz,G);
    //	cout<<x<<" "<<G<<endl;
    	pq[1][G].Insert(0);
    	for(int i=hd[G];i!=-1;i=nxt[i])
    		if(!ban[i]){
    			ban[i]=ban[i^1]=1;
    			Heap tmp;dfs(to[i],-1,1,tmp);
    			int Tmp=divide(to[i]);
    			fa[Tmp]=G;pq[0][Tmp]=tmp;
    			pq[1][G].Insert(pq[0][Tmp].Top());
    		}
    	Insert(pq[1][G]);
    	return G;
    }
    
    inline void dfs(int x,int FA){
    	for(int i=hd[x];i!=-1;i=nxt[i])
    		if(to[i]!=FA)
    			f[to[i]][0]=x,dep[to[i]]=dep[x]+1,dfs(to[i],x);
    }
    
    inline void init(void){
    	for(int j=1;j<=20;j++)
    		for(int i=1;i<=n;i++)
    			f[i][j]=f[f[i][j-1]][j-1];
    }
    
    inline int lca(int x,int y){
    	if(dep[x]<dep[y]) swap(x,y);
    	int d=dep[x]-dep[y];
    	for(int i=20;i>=0;i--)
    		if((d>>i)&1) x=f[x][i];
    	if(x==y) return x;
    	for(int i=20;i>=0;i--)
    		if(f[x][i]!=f[y][i])
    			x=f[x][i],y=f[y][i];
    	return f[x][0];
    }
    
    inline int getdis(int x,int y){
    	return dep[x]+dep[y]-2*dep[lca(x,y)];
    }
    
    inline void TurnOn(int x){
    	Erase(pq[1][x]);
    	pq[1][x].Erase(0);
    	Insert(pq[1][x]);
    	for(int i=x;fa[i];i=fa[i]){
    		Erase(pq[1][fa[i]]);
    		if(pq[0][i].size())
    			pq[1][fa[i]].Erase(pq[0][i].Top());
    		pq[0][i].Erase(getdis(x,fa[i]));
    		if(pq[0][i].size())
    			pq[1][fa[i]].Insert(pq[0][i].Top());
    		Insert(pq[1][fa[i]]);
    	}
    }
    
    inline void TurnOff(int x){
    	Erase(pq[1][x]);
    	pq[1][x].Insert(0);
    	Insert(pq[1][x]);
    	for(int i=x;fa[i];i=fa[i]){
    		Erase(pq[1][fa[i]]);
    		if(pq[0][i].size())
    			pq[1][fa[i]].Erase(pq[0][i].Top());
    		pq[0][i].Insert(getdis(x,fa[i]));
    		if(pq[0][i].size())
    			pq[1][fa[i]].Insert(pq[0][i].Top());
    		Insert(pq[1][fa[i]]);
    	}
    }
    
    signed main(void){
    #ifndef ONLINE_JUDGE
    	freopen("in.txt","r",stdin);
    #endif
    	scanf("%d",&n);memset(hd,-1,sizeof(hd));
    	for(int i=1,x,y;i<n;i++) scanf("%d%d",&x,&y),add(x,y),add(y,x);
    	divide(1),dfs(1,-1),init();
    	scanf("%d",&q);tot=n;
    	for(int i=1;i<=n;i++) co[i]=1;
    	for(int i=1,x;i<=q;i++){
    		scanf("%s",opt);
    		if(opt[0]=='C'){
    			scanf("%d",&x);
    			if(co[x]) TurnOn(x),tot--,co[x]=0;
    			else TurnOff(x),tot++,co[x]=1;
    		}
    		else{
    			if(tot<=1) printf("%d
    ",tot-1);
    			else printf("%d
    ",ans.Top());
    		}
    	}
    	return 0;
    }
    

    括号序列:

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    //by NeighThorn
    #define inf 0x3f3f3f3f
    using namespace std;
    
    const int maxn=300000+5;
    
    int n,m,no,cnt,tot,co[maxn],hd[maxn],to[maxn],nxt[maxn],seq[maxn],pos[maxn];
    
    char opt[3];
    
    struct M{
    	
    	int l,r,a,b,lp,lm,rp,rm,dis;
    	
    	inline void init(int val){
    		lp=rp=lm=rm=dis=val;
    	}
    	
    }tree[maxn<<2];
    
    struct SegmentTree{
    	
    	inline M merge(M x,M y){
    		M ans;ans.l=x.l,ans.r=y.r;
    		if(x.b>y.a) ans.a=x.a,ans.b=y.b+x.b-y.a;
    		else        ans.a=x.a+y.a-x.b,ans.b=y.b;
    		ans.lp =max(x.lp,max(y.lp-x.b+x.a,y.lm+x.b+x.a));
    		ans.lm =max(x.lm,y.lm+x.b-x.a);
    		ans.rp =max(y.rp,max(x.rp-y.a+y.b,x.rm+y.b+y.a));
    		ans.rm =max(y.rm,x.rm+y.a-y.b);
    		ans.dis=max(max(x.dis,y.dis),max(x.rp+y.lm,x.rm+y.lp));
    		return ans;
    	}
    	
    	inline void build(int l,int r,int tr){
    		tree[tr].l=l,tree[tr].r=r;
    		if(l==r){
    			tree[tr].init(-inf);
    			tree[tr].a=tree[tr].b=0;
    			if(seq[l]==-1) tree[tr].b=1;
    			else if(seq[l]==-2) tree[tr].a=1;
    			else tree[tr].init(0);
    			return;
    		}
    		int mid=(l+r)>>1;
    		build(l,mid,tr<<1),build(mid+1,r,tr<<1|1);
    		tree[tr]=merge(tree[tr<<1],tree[tr<<1|1]);
    	}
    	
    	inline void change(int p,int tr){
    		if(tree[tr].l==tree[tr].r){
    			if(co[seq[p]])
    				co[seq[p]]=0,tree[tr].init(-inf);
    			else
    				co[seq[p]]=1,tree[tr].init(   0);
    			return;
    		}
    		int mid=(tree[tr].l+tree[tr].r)>>1;
    		if(p<=mid) change(p,tr<<1);
    		else       change(p,tr<<1|1);
    		tree[tr]=merge(tree[tr<<1],tree[tr<<1|1]);
    	}
    	
    }seg;
    
    inline void add(int x,int y){
    	to[cnt]=y;nxt[cnt]=hd[x];hd[x]=cnt++;
    }
    
    inline void dfs(int x,int fa){
    	seq[++tot]=-1;pos[seq[++tot]=x]=tot;
    	for(int i=hd[x];i!=-1;i=nxt[i])
    		if(to[i]!=fa)
    			dfs(to[i],x);
    	seq[++tot]=-2;
    }
    
    signed main(void){
    #ifndef ONLINE_JUDGE
    	freopen("in.txt","r",stdin);
    #endif
    	scanf("%d",&n);cnt=0;
    	memset(hd,-1,sizeof(hd));no=n;
    	for(int i=1;i<=n;i++) co[i]=1;
    	for(int i=1,x,y;i<n;i++)
    		scanf("%d%d",&x,&y),add(x,y),add(y,x);
    	dfs(1,-1);seg.build(1,tot,1);scanf("%d",&m);
    	for(int i=1,x;i<=m;i++){
    		scanf("%s",opt);
    		if(opt[0]=='G'){
    			if(no<=1) printf("%d
    ",no-1);
    			else printf("%d
    ",tree[1].dis);
    		}
    		else
    			scanf("%d",&x),no-=co[x],seg.change(pos[x],1),no+=co[x];
    	}
    	return 0;	
    }
    

    By NeighThorn

  • 相关阅读:
    TreeMap<K,V>类
    2020-3-7学习地图
    Thread类
    Redis-Windows中注册Redis后台守护进程
    Redsi-(error) NOAUTH Authentication required.
    2020-3-6学习地图
    Collection<E>接口
    Map<K,V>接口
    Set接口
    List类
  • 原文地址:https://www.cnblogs.com/neighthorn/p/6626060.html
Copyright © 2011-2022 走看看