1095: [ZJOI2007]Hide 捉迷藏

思路看得题解:
考虑将整个树dfs下来,得到一个括号序列,对于两个点,他们之间的距离就是他们之间的括号数量去掉中间匹配的括号的数量
那么考虑线段树维护,用s表示左括号数量,s2表示右括号数量, ri li r i   l i 分别表示区间黑点左括号最多的数量/右括号最多的数量, rm lm r m   l m 分别表示区间到右端点/左端点括号最多的数量,容易发现,当左边放上左括号,右边的区间是随便放左右括号的,当右边的区间放上右括号,左边的区间也是随便发括号的,那么统计即可
复杂度 O(qlogn) O ( q log ⁡ n )
c++代码如下:

#include<bits/stdc++.h>
#define rep(i,x,y) for(register int i = x; i <= y; ++ i)
#define repd(i,x,y) for(register int i = x; i >= y; -- i)
using namespace std;
typedef long long ll;
template<typename T>inline void read(T&x) 
{
    char c;int sign = 1;x = 0;
    do { c = getchar(); if(c == '-') sign = -1; }while(!isdigit(c));
    do { x = x * 10 + c - '0'; c = getchar(); }while(isdigit(c));
    x *= sign;
}

const int N = 3e5+50,inf = 1e9;
int head[N],nxt[N << 1],to[N << 1],tot;
int n,q,sz,w[N],id[N];

inline void add(int x,int y)
{
    nxt[tot] = head[x];
    to[tot] = y;
    head[x] = tot++;
}

struct Segment_tree
{
    int ans[N<<2],s[N << 2],s2[N << 2];
    int ri[N << 2],rm[N << 2],li[N << 2],lm[N << 2];

    inline void update(int id)
    {
        li[id] = max(li[id<<1],s[id<<1] - s2[id<<1] + li[id<<1|1]);
        ri[id] = max(ri[id<<1|1],s2[id<<1|1] - s[id<<1|1] + ri[id<<1]);
        lm[id] = max(lm[id<<1],max(s[id<<1] + s2[id<<1] + li[id<<1|1],s2[id<<1] + lm[id<<1|1] - s[id<<1]));
        rm[id] = max(rm[id<<1|1],max(s[id<<1|1] + s2[id<<1|1] + ri[id<<1],s[id<<1|1] + rm[id<<1] - s2[id<<1|1])); 
        ans[id] = max(max(ans[id<<1],ans[id<<1|1]),max(li[id<<1|1] + rm[id<<1],lm[id<<1|1] + ri[id<<1]));
    }

    void build(int id,int l,int r)
    {
        if(l == r)
        {
            if(!w[l]) ri[id] = rm[id] = li[id] = lm[id] = w[l];
            else{ ri[id] = li[id] = rm[id] = lm[id] = -inf,ans[id] = -1; if(w[l] == -1) ++s2[id]; else ++ s[id];}
            return ;
        }
        int mid = l + r>> 1;
        build(id<<1,l,mid); build(id<<1|1,mid+1,r);

        s[id] = s[id<<1|1] + max(0,s[id<<1] - s2[id<<1|1]);
        s2[id] = s2[id<<1] + max(0,s2[id<<1|1] - s[id<<1]);
        update(id); 
    }

    void modify(int id,int l,int r,int pos)
    {
        if(l == r) 
        {
            if(w[l]) ri[id] = rm[id]= li[id] = lm[id] = ans[id] = w[l] = 0;
            else w[l] = 1,ri[id] = li[id] = rm[id] = lm[id] = -inf,ans[id] = -1;
            return ;
        }
        int mid = l + r>>1;
        if(pos <= mid) modify(id<<1,l,mid,pos);
        else modify(id<<1|1,mid+1,r,pos);
        update(id);
    }
}seg;

void dfs(int x,int f)
{
    w[++sz] = 1; id[x] = ++ sz;
    for(register int i = head[x];~i;i=nxt[i])
        if(to[i] != f) dfs(to[i],x);
    w[++sz] = -1;
}

char s[2];

int main()
{
    memset(head,-1,sizeof head);
    read(n);
    rep(i,2,n)
    {
        int u,v;
        read(u); read(v);
        add(u,v); add(v,u);
    }

    dfs(1,1);

    seg.build(1,1,sz);
    read(q);
    while(q--)
    {
        scanf("%s",s);int x;
        if(s[0] == 'C') read(x),seg.modify(1,1,sz,id[x]);
        if(s[0] == 'G') printf("%d\n",seg.ans[1]);
    }

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值