bzoj1095

1095: [ZJOI2007]Hide 捉迷藏

Time Limit: 40 Sec   Memory Limit: 162 MB
Submit: 1602   Solved: 650
[ 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。


论文题,非常恶心!线段树需要维护7个域,

区间有括号是rk,区间左括号数lk,以黑点结束的括号序列的前缀和la,前缀差(是逆的)lb,以黑点开始的括号序列的后缀和ra,后缀差rb,两个黑点之间最大的括号序列的长度。

首先明白]]][[[的含义,]表示往上走,[表示往下走,因为序列长度就是距离了。

然后树中的一个点拆成3个时间戳'[','颜色',']',这个通过dfs得到

然后难度在线段树的区间合并。假设序列]]][[[用(a1,b1)表示,其中a1为]的个数,b1为[的个数,此时需要合并(a1,b1),(a2,b2),如果b1=a2,中间的[[[]]]相消,于是变成(a1,b2),否则必然多出来]或者[与a1或者b2合并。

那么长度变化显然是a1+b2+abs(a2-b1),讨论a2和b1的大小, 于是组合出了四种东西la,lb,ra,rb..这个推导还是有点烦,最好直接看代码,自己推推。


代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#define ls id<<1,l,mid
#define rs id<<1|1,mid+1,r
#define Maxn 100010
using namespace std;

inline int read(){
    int x=0;
    char ch=getchar();
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9'){
        x=x*10+ch-'0';
        ch=getchar();
    }
    return x;
}
struct edge{
    int to,next;
}p[Maxn<<1];
int tot;
int head[Maxn];
void addedge(int u,int v){
    p[tot].to=v;
    p[tot].next=head[u];
    head[u]=tot++;
}
int dfn[Maxn*3],pos[Maxn*3],c[Maxn*3];
int tmpdfn;
void dfs(int u,int fa){
    dfn[++tmpdfn]=-1;
    dfn[++tmpdfn]=u;
    pos[u]=tmpdfn; //u的时间戳
    for(int i=head[u];i!=-1;i=p[i].next){
        int v=p[i].to;
        if(v==fa) continue;
        dfs(v,u);
    }
    dfn[++tmpdfn]=-2;
}
const int inf=-0x3f3f3f3f;
struct seg{
    int lk,rk,la,lb,ra,rb,d;
    seg(){}
    seg(int _lk,int _rk,int _la,int _lb,int _ra,int _rb,int _d){
        lk=_lk,rk=_rk,la=_la,lb=_lb,ra=_ra,rb=_rb,d=_d;
    }
    seg operator+(seg a){
        int LK=lk+max(a.lk-rk,0),RK=a.rk+max(rk-a.lk,0);
        int LA=la,LB=lb,RA=a.ra,RB=a.rb; //la前缀和,lb前缀逆差
        //a1+b2+abs(b1-a2)
        if(a.lb!=inf) LA=max(LA,lk+rk+a.lb);
        if(a.la!=inf) LA=max(LA,lk-rk+a.la);
        if(ra!=inf) RA=max(RA,ra+a.rk-a.lk);
        if(rb!=inf) RA=max(RA,rb+a.rk+a.lk);
        //b1+b2-a1-a2
        if(a.lb!=inf) LB=max(LB,rk-lk+a.lb);
        //a1+a2-b1-b2
        if(rb!=inf) RB=max(RB,rb+a.lk-a.rk);
        int D=max(d,a.d);
        if(ra!=inf&&a.lb!=inf) D=max(D,ra+a.lb);
        if(rb!=inf&&a.la!=inf) D=max(D,rb+a.la);
        return seg(LK,RK,LA,LB,RA,RB,D);
    }
}tr[Maxn*12];
void pushup(int id){
    tr[id]=tr[id<<1]+tr[id<<1|1];
}
void set(int id,int l){
    tr[id].lk=dfn[l]==-2;
    tr[id].rk=dfn[l]==-1;
    tr[id].d=inf;
    if(dfn[l]>0&&c[l]) tr[id].la=tr[id].lb=tr[id].ra=tr[id].rb=0;
    else tr[id].la=tr[id].lb=tr[id].ra=tr[id].rb=inf;
}
void build(int id,int l,int r){
    if(l==r){
        set(id,l);
        return;
    }
    int mid=l+r>>1;
    build(ls);
    build(rs);
    pushup(id);
}
void update(int id,int l,int r,int a){
    if(l==r){
        set(id,l);
        return;
    }
    int mid=l+r>>1;
    if(a<=mid) update(ls,a);
    else update(rs,a);
    pushup(id);
}
int main()
{
    int n,m,u,v;
    n=read();
    tot=0;
    memset(head,-1,sizeof head);
    for(int i=1;i<n;i++){
        u=read(),v=read();
        addedge(u,v);
        addedge(v,u);
    }
    tmpdfn=0;
    dfs(1,0);
    for(int i=1;i<=n;i++) c[pos[i]]=1;
    build(1,1,tmpdfn);
    m=read();
    int num=n;
    char s[2];
    while(m--){
        scanf("%s",s);
        if(s[0]=='G'){
            if(num==0) puts("-1");
            else if(num==1) puts("0");
            else printf("%d\n",tr[1].d);
        }
        else{
            u=read();
            if(c[pos[u]]==1) num--;
            else num++;
            c[pos[u]]^=1;
            update(1,1,tmpdfn,pos[u]);
        }
    }
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值