1095: [ZJOI2007]Hide 捉迷藏

1095: [ZJOI2007]Hide 捉迷藏

Time Limit: 40 Sec   Memory Limit: 256 MB
Submit: 2638   Solved: 1088
[ 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

[ Submit][ Status][ Discuss]

用线段树维护一棵树的括号序列
论文在此
树中每个点在dfs第一次遍历到时增加一个左括号,然后放入这个点,当它的所有子孙遍历完,返回它时,添加一个右括号,这样查询树上两点的距离,就是这两个点之间括号序列匹配后剩下的序列的长度
这个序列一定是二元组(a,b)的形式,其中,a代表右括号个数,b代表左括号个数

定义如下函数
Dis(S) = max{a' + b'|S'两端为黑色节点,是S的一个子串}
定义suf_plus(S) = max{a' + b'|S'以黑点开头,是S的一个后缀}
定义suf_minus(S) = max{a' - b'|S'以黑点开头,是S的一个后缀}
定义pre_plus(S) = max{a' + b'|S'以黑点结尾,是S的一个前缀}
定义pre_minus(S) = max{b' - a'|S'以黑点结尾,是S的一个前缀}
A[o]:节点O对应区间内,左括号个数
B[o]:节点O对应区间内,右括号个数

合并左右两个括号序列的公式不难推导,然后引申出三个结论
a + b = a1 + b2 + |a2 - b1| = max{a1 + b2 + a2 - b1,a1 + b2 - a2 + b1}
a - b = a1 - b1 + a2 - b2
b - a = b1 - a1 + b2 - a2

用以上三个结论可推出所有函数的公式
Dis(s) = max{Dis(sl),Dis(sr),suf_plus(s1) + pre_minus(sr),suf_minus(sl) + pre_plus(sr)}
suf_plus(s) = max{suf_plus(sr),suf_minus(sl) + c + d,suf_plus(sl) - c + d}
suf_minus(s) = max{suf_minus(sr),suf_minus(sl) + c - d}
pre_plus(s) = max{pre_plus(sl),a - b + pre_plus(sr),a + b + pre_minus(sr)}
pre_minus(s) = max{pre_minus(sl),b - a + pre_minus(sr)}

这里不再赘述
实际处理时,,线段树内初值如下
初值:黑点dis = -INF其余为0  白点和括号全为-INF
#include<iostream>
#include<cstdio>
#include<queue>
#include<vector>
#include<bitset>
#include<algorithm>
#include<cstring>
#include<map>
#include<stack>
#include<set>
#include<cmath>
#include<ext/pb_ds/priority_queue.hpp>
using namespace std;
 
const int maxn = 3E5 + 10;
const int T = 10;
const int INF = 1E5;
 
int n,m,dfs_clock,Dis[maxn*T],suf_plus[maxn*T],suf_minus[maxn*T],Now[maxn]
    ,pre_plus[maxn*T],pre_minus[maxn*T],A[maxn*T],B[maxn*T],pos[maxn];
 
vector <int> v[maxn];
 
void maintain(int o)
{
    int lc = o<<1,rc = o<<1|1;
    if (B[rc] >= A[lc]) B[o] = B[lc] + B[rc] - A[lc],A[o] = A[rc];
    else A[o] = A[lc] - B[rc] + A[rc],B[o] = B[lc];
    Dis[o] = max(Dis[lc],Dis[rc]);
    Dis[o] = max(Dis[o],suf_plus[lc] + pre_minus[rc]);
    Dis[o] = max(Dis[o],suf_minus[lc] + pre_plus[rc]);
    suf_plus[o] = max(suf_plus[rc],suf_minus[lc] + B[rc] + A[rc]);
    suf_plus[o] = max(suf_plus[o],suf_plus[lc] - B[rc] + A[rc]);
    suf_minus[o] = max(suf_minus[rc],suf_minus[lc] + B[rc] - A[rc]);
    pre_plus[o] = max(pre_plus[lc],B[lc] - A[lc] + pre_plus[rc]);
    pre_plus[o] = max(pre_plus[o],B[lc] + A[lc] + pre_minus[rc]);
    pre_minus[o] = max(pre_minus[lc],A[lc] - B[lc] + pre_minus[rc]);
}
 
void Insert(int o,int l,int r,int posi,int typ)
{
    if (l == r) {
        if (typ == 0) {
            A[o] = 1; Dis[o] = -INF;
            suf_plus[o] = suf_minus[o] = -INF;
            pre_plus[o] = pre_minus[o] = -INF;
        }
        else if (typ == 1) {
            B[o] = 1; Dis[o] = -INF;
            suf_plus[o] = suf_minus[o] = -INF;
            pre_plus[o] = pre_minus[o] = -INF;
        }
        else Dis[o] = -INF;
        return;
    }
    int mid = (l + r) >> 1;
    if (posi <= mid) Insert(o<<1,l,mid,posi,typ);
    else Insert(o<<1|1,mid+1,r,posi,typ);
    maintain(o);
}
 
void Dfs(int x,int from)
{
    Insert(1,1,3*n,++dfs_clock,0);
    pos[x] = ++dfs_clock;
    Insert(1,1,3*n,pos[x],2);
    for (int i = 0; i < v[x].size(); i++) {
        int to = v[x][i];
        if (to == from) continue;
        Dfs(to,x);
    }
    Insert(1,1,3*n,++dfs_clock,1);
}
 
void Modify(int o,int l,int r,int posi,int typ)
{
    if (l == r) {
        if (!typ) {
            Dis[o] = -INF;
            suf_plus[o] = suf_minus[o] = -INF;
            pre_plus[o] = pre_minus[o] = -INF;
             
        }
        else {
            Dis[o] = -INF;
            suf_plus[o] = suf_minus[o] = 0;
            pre_plus[o] = pre_minus[o] = 0;
        }
        return;
    }
    int mid = (l + r) >> 1;
    if (posi <= mid) Modify(o<<1,l,mid,posi,typ);
    else Modify(o<<1|1,mid+1,r,posi,typ);
    maintain(o);
}
 
int getint()
{
    char ch = getchar();
    int ret = 0;
    while (ch < '0' || '9' < ch) ch = getchar();
    while ('0' <= ch && ch <= '9')
        ret = ret*10 + ch - '0',ch = getchar();
    return ret;
}
 
int getcom()
{
    char ch = getchar();
    while (ch != 'G' && ch != 'C') ch = getchar();
    return ch == 'G'?1:2;
}
 
int main()
{
    #ifdef DMC
        freopen("DMC.txt","r",stdin);
    #endif
     
    n = getint();
    for (int i = 1; i < n; i++) {
        int x = getint(),y = getint();
        v[x].push_back(y);
        v[y].push_back(x);
    }
    Dfs(n/2,0);
    m = getint();
    while (m--) {
        int com = getcom();
        if (com == 1) printf("%d\n",Dis[1]);
        else {
            int x = getint();
            Modify(1,1,3*n,pos[x],Now[x]);
            Now[x] ^= 1;
        }
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值