列表

详情


NC20470. [ZJOI2007]HIDE 捉迷藏

描述

捉迷藏 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。

示例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

原站题解

上次编辑到这里,代码来自缓存 点击恢复默认模板

C++(g++ 7.5.0) 解法, 执行用时: 3264ms, 内存消耗: 179784K, 提交时间: 2022-09-22 20:38:44

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <queue>

using namespace std ;

using ll = long long ;
using pii = pair<int,int> ;

const int N = 4e5 + 100 , M = 2 * N ;
const int INF = N ;

// 题目是要询问任意两个关着灯之间的最长距离
// 那么我们进行维护,对于每一个重心来说,我们进行对每一个儿子维护一个最大值
// 将与重心直接相连接的子树中最大值插入当前重心所维护的信息中
// 然后为了方便,我们直接相连的子树肯定是另一个,所以我们直接进行计算
// 然后我们还使用一个变量来维护全局的最大值

int n,m ;
int h[N],e[M],ne[M],idx ;
int sz[N],dist[N] ;
bool st[N],state[N] ;
struct Dui{
    priority_queue<int> q,del ;

    void insert(int x){
        q.push(x) ;
    }

    void del_c(int x){
        del.push(x) ;
    }

    int get_max(){
        while(!q.empty() && !del.empty() && q.top() == del.top()) q.pop(),del.pop() ;
        if(q.empty()) return -INF ;
        return q.top() ;
    }

    int get_mx2(){
        int tot = get_max() ;
        if(tot == -INF) return -INF ;
        q.pop() ;
        int ans = get_max() ;
        q.push(tot) ;
        return ans ;
    }
};
int divfa[N] ;
Dui global ; // 存储全局的信息
Dui zz[N] ; // 存储当前连通块的最大值
Dui grz[N] ; // 存储与重心直接相连接的所有子树的最大值
struct Info{
    int dist,gr ;
};
vector<Info> po[N] ;

void add(int a,int b){
    e[idx] = b,ne[idx] = h[a],h[a] = idx ++ ;
}

int getsz(int u,int fa){
    int tot = 1 ;
    for(int i = h[u] ; ~ i ; i = ne[i]){
        int j = e[i] ;
        if(j == fa || st[j]) continue ;
        tot += getsz(j,u) ;
    }
    return tot ;
}

int getgravity(int u,int fa,int sum){
    int gr = -1,mx = 0 ;
    sz[u] = 1 ;
    for(int i = h[u] ; ~  i ; i = ne[i]){
        int j = e[i] ;
        if(j == fa || st[j]) continue ;
        int nx = getgravity(j,u,sum) ;
        if(nx != -1) gr = nx ;
        sz[u] += sz[j] ;
        mx = max(mx,sz[j]) ;
    }
    mx = max(mx,sum - sz[u]) ;
    if(2 * mx <= sum) gr = u ;
    return gr ;
}

void dfs(int u,int fa,int dist,int gr){
    po[u].push_back({dist,gr}) ;
    zz[gr].insert(dist) ;
    for(int i = h[u] ;~ i ; i = ne[i]){
        int j = e[i] ;
        if(j == fa || st[j]) continue ;
        dfs(j,u,dist+1,gr) ;
    }
}

void divtree(int u,int fa){
    int tot = getsz(u,-1) ;
    int gr = getgravity(u,-1,tot) ;
    divfa[gr] = fa ;
    grz[gr].insert(0) ; // 插入当前节点
    if(fa != -1){
        dfs(u,-1,1,gr) ;
        grz[fa].insert(zz[gr].get_max()) ;
    }
    st[gr] = 1 ;

    for(int i = h[gr] ; ~ i ; i = ne[i]){
        int j = e[i] ;
        if(j == fa || st[j]) continue ;
        divtree(j,gr) ;
    }

    global.insert(grz[gr].get_max() + grz[gr].get_mx2()) ;
}

int main(){
    scanf("%d",&n) ;
    memset(h,-1,sizeof h) ;
    for(int i = 1 ; i <= n - 1 ; i ++){
        int a,b ;
        scanf("%d%d",&a,&b) ;
        add(a,b),add(b,a) ;
    }
    divtree(1,-1) ;
    scanf("%d",&m) ;
    int cnt = n ;
    while(m --){
        char op ;
        scanf(" %c",&op) ;
        if(op == 'G'){
            if(cnt == 0) puts("-1") ;
            else if(cnt == 1) puts("0") ;
            else
                printf("%d\n",global.get_max()) ;
        }
        else{
            int x ;
            scanf("%d",&x) ;

            // 改变第x个灯的状态
            for(Info t : po[x]){
                if(!state[x]){ // 代表原先是关着现在需要打开
                    // 需要删除这个点
                    int tot = zz[t.gr].get_max() ;
                    zz[t.gr].del_c(t.dist) ;
                    if(divfa[t.gr] != -1){
                        global.del_c(grz[divfa[t.gr]].get_max() + grz[divfa[t.gr]].get_mx2()) ;
                        grz[divfa[t.gr]].del_c(tot) ;
                        tot = zz[t.gr].get_max() ;
                        grz[divfa[t.gr]].insert(tot) ;

                        global.insert(grz[divfa[t.gr]].get_max() + grz[divfa[t.gr]].get_mx2()) ;
                    }
                }
                else{  // 代表原先是打开,现在需要关闭
                    int tot = zz[t.gr].get_max() ;
                    zz[t.gr].insert(t.dist) ;
                    if(divfa[t.gr] != -1){
                        global.del_c(grz[divfa[t.gr]].get_max() + grz[divfa[t.gr]].get_mx2()) ;
                        grz[divfa[t.gr]].del_c(tot) ;
                        tot = zz[t.gr].get_max() ;
                        grz[divfa[t.gr]].insert(tot) ;
                        global.insert(grz[divfa[t.gr]].get_max() + grz[divfa[t.gr]].get_mx2()) ;
                    }
                }
            }

            if(!state[x]){
                global.del_c(grz[x].get_max() + grz[x].get_mx2()) ;
                grz[x].del_c(0) ;
                global.insert(grz[x].get_max() + grz[x].get_mx2()) ;
                
                cnt -- ;
                state[x] = 1 ;
            }
            else{ // 关灯灯
                global.del_c(grz[x].get_max() + grz[x].get_mx2()) ;
                grz[x].insert(0) ;
                global.insert(grz[x].get_max() + grz[x].get_mx2()) ;
                cnt ++ ;
                state[x] = 0 ;
            }
        }
    }
    return 0 ;
}

C++11(clang++ 3.9) 解法, 执行用时: 649ms, 内存消耗: 43348K, 提交时间: 2019-03-09 17:55:33

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<set>
#include<ctime>
#include<queue>
#include<cmath>
#include<algorithm>
#define inf 1000000000
#define ll long long
using namespace std;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
char ch[10];
int n,q,cnt,tot,black;
int last[100005];
int v[300005],pos[100005],c[100005];
struct edge{int to,next;}e[200005];
struct seg{
	int l,r,l1,r1,l2,r2,dis,c1,c2;
	void ini(int x){
		dis=-inf;
		c1=c2=0;
		if(v[x]==-6)c2=1;
		if(v[x]==-9)c1=1;
		if(v[x]>0&&c[v[x]])
			l1=l2=r1=r2=0;
		else l1=l2=r1=r2=-inf;
		}
}t[1200005];
inline int max(int a,int b,int c)
{
	return max(max(a,b),c);
}
void insert(int u,int v)
{
	e[++cnt].to=v;e[cnt].next=last[u];last[u]=cnt;
	e[++cnt].to=u;e[cnt].next=last[v];last[v]=cnt;
}
void dfs(int x,int fa)
{
	v[++tot]=-6;
	v[++tot]=x;
	pos[x]=tot;
	for(int i=last[x];i;i=e[i].next)
		if(e[i].to!=fa)
			dfs(e[i].to,x);
	v[++tot]=-9;
}
inline seg merge(seg s1,seg s2)
{
	seg s;
	int a=s1.c1,b=s1.c2,c=s2.c1,d=s2.c2;
	s.l=s1.l;s.r=s2.r;

	s.dis=max(s1.dis,s2.dis);
	s.dis=max(s.dis,s1.r1+s2.l2,s1.r2+s2.l1);

	if(b<c)s.c1=a-b+c,s.c2=d;
	else s.c1=a,s.c2=b-c+d;

	s.r1=max(s2.r1,s1.r1-c+d,s1.r2+c+d);
	s.r2=max(s2.r2,s1.r2+c-d);

	s.l1=max(s1.l1,s2.l1-b+a,s2.l2+b+a);
	s.l2=max(s1.l2,s2.l2+b-a);

	return s;
}
void build(int k,int l,int r)
{
	if(l==r)
	{
		t[k].l=l;t[k].r=r;
		t[k].ini(l);
		return;
	}
	int mid=(l+r)>>1;
	build(k<<1,l,mid);
	build(k<<1|1,mid+1,r);
	t[k]=merge(t[k<<1],t[k<<1|1]);
}
void modify(int k,int x)
{
	int l=t[k].l,r=t[k].r;
	if(l==r){t[k].ini(l);return;}
	int mid=(l+r)>>1;
	if(x<=mid)modify(k<<1,x);
	else modify(k<<1|1,x);
	t[k]=merge(t[k<<1],t[k<<1|1]);
}
int main()
{
	black=n=read();
	for(int i=1;i<=n;i++)c[i]=1;
	for(int i=1;i<n;i++)
	{
		int u=read(),v=read();
		insert(u,v);
	}
	dfs(1,0);
	build(1,1,tot);
	q=read();
	for(int i=1;i<=q;i++)
	{
		scanf("%s",ch);
		if(ch[0]=='C')
		{
			int x=read();
			if(c[x])black--;
			else black++;
			c[x]^=1;
			modify(1,pos[x]);
		}
		else 
		{
			if(!black)puts("-1");
			else if(black==1)puts("0");
			else printf("%d\n",t[1].dis);
		}
	}
	return 0;
}

上一题