bzoj 1095(括号序列)

本文介绍了一种使用括号序列和线段树解决树上两点间距离问题的方法。通过将树节点的访问过程转化为括号序列,利用线段树维护括号的匹配状态,实现了高效查询两点间距离的功能。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门
括号序列:访问到一个点时,加入’(‘,再加入该点编号,访问完这个点及其子树退出时加入’)’
括号序列有一个神奇的性质:任意两点间的距离即为括号序列中将它们之间可匹配的括号全部消去后,剩下的括号的数量。为什么呢???把序列中可以匹配的括号消去后,剩余的括号肯定是”)))…..)(……(((“这样的。’)’的个数就是x到lca(x,y)的距离,’(‘的个数就是y到lca(x,y)的距离。

用线段树维护括号序列,线段树每个结点维护七个元素:
s1:”(“的个数
s2:”)”的个数
l1:左段括号数量最大值(去除匹配括号的)
r1:右段括号数量最大值
l2:左段”(“个数与”)”个数之差最大值
r2:右段”)”个数与”(“个数之差最大值
ans:该区间内去匹配括号后”(“个数与”)”个数之和的最大值。

#include<bits/stdc++.h>
#define lson rt<<1,l,mid
#define rson rt<<1|1,mid+1,r
#define root 1,1,tim
using namespace std;
const int maxn=1e5+2,INF=0x3f3f3f3f;
int n,m,mark[maxn],cnt;
struct NODE {
    int s1,s2,l1,r1,l2,r2,ans;
    inline void f(int rt) {
        s1=s2=0;
        l1=r1=l2=r2=ans=-INF;
        if (rt==-1) s1=1;
        else if (rt==-2) s2=1;
        else if (mark[rt]==1) l1=r1=l2=r2=ans=0;
        //使更新序列中的实点(不是括号的点)为有效操作,否则-0x3f3f3f3f加来加去还是很大的负数。。。
    }
    inline void pushup(NODE &l,NODE &r) {
        s1=r.s1+max(0,l.s1-r.s2),
        s2=l.s2+max(0,r.s2-l.s1),
        l1=max(l.l1,max(l.s1+l.s2+r.l2,l.s2+r.l1-l.s1)),
        r1=max(r.r1,max(r.s1+r.s2+l.r2,r.s1+l.r1-r.s2)),
        l2=max(l.l2,r.l2+l.s1-l.s2),
        r2=max(r.r2,l.r2+r.s2-r.s1),
        ans=max(max(l.ans,r.ans),max(l.r1+r.l2,r.l1+l.r2));
    }
}s[maxn*3<<2];
int num[maxn*3],tim=0;
int head[maxn],edge=0,pos[maxn];
struct EDGE {
    int v,nxt;
}e[maxn<<1];
inline void adde(int u,int v) {
    e[edge].v=v,e[edge].nxt=head[u],head[u]=edge++;
    e[edge].v=u,e[edge].nxt=head[v],head[v]=edge++;
}
void dfs(int p,int fa) {
    num[++tim]=-1;
    pos[num[++tim]=p]=tim;
    for (int i=head[p];~i;i=e[i].nxt) {
        int v=e[i].v;
        if (v!=fa) dfs(v,p);
    }
    num[++tim]=-2;
}
void modify(int rt,int l,int r,int p) {
    if (l==r) {s[rt].f(num[p]);return ;}
    int mid=(l+r)>>1;
    if (p<=mid) modify(lson,p);
    else modify(rson,p);
    s[rt].pushup(s[rt<<1],s[rt<<1|1]);
}
void build(int rt,int l,int r) {
    if (l==r) {s[rt].f(num[l]);return ;}
    int mid=(l+r)>>1;
    build(lson),
    build(rson),
    s[rt].pushup(s[rt<<1],s[rt<<1|1]);
}
inline int read() {
    int x=0;char c=getchar();
    while (c<'0'||c>'9') c=getchar();
    while (c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
    return x;
}
int main() {
//  freopen("bzoj 1095.in","r",stdin);
    memset(head,-1,sizeof(head));
    cnt=n=read();
    for (register int i=1;i<=n;++i) mark[i]=1;//black
    for (register int i=1;i<n;++i) {
        int u=read(),v=read();
        adde(u,v);
    }
    dfs(1,0);
    build(root);
    m=read();
    for (register int i=1;i<=m;++i) {
        char ss[2];
        scanf("%s",ss);
        if (ss[0]=='C') {
            int u=read();
            cnt+=mark[u]=-mark[u];
            modify(root,pos[u]);
        }
        else {
            if (cnt==0) puts("-1");//no black points
            else if (cnt==1) puts("0");//only one black point
            else printf("%d\n",s[1].ans);//ans has been maintained
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值