BZOJ 1095 捉迷藏(线段树维护括号序列)
对于树的一个括号序列,树上两点的距离就是在括号序列中两点之间的括号匹配完之后的括号数。。。
由此可以得出线段树的做法。。
#include<cstdio> #include<iostream> #define maxn 100000 #define inf (1<<25) using namespace std; int vis[maxn+10]; struct node{ int l1,l2,r1,r2,c1,c2,dis; void val(int x){ c1=c2=0; l1=l2=r1=r2=dis=-inf; if(x==-1)c2=1; else if(x==-2)c1=1; else if(vis[x]==1)l1=l2=r1=r2=0; } void merge(node &a,node &b){ c1=a.c1+max(0,b.c1-a.c2); c2=b.c2+max(0,a.c2-b.c1); dis=max(max(a.dis,b.dis),max(a.r1+b.l2,a.r2+b.l1)); l1=max(a.l1,max(b.l1-a.c2+a.c1,b.l2+a.c2+a.c1)); l2=max(a.l2,b.l2+a.c2-a.c1); r1=max(b.r1,max(a.r1-b.c1+b.c2,a.r2+b.c1+b.c2)); r2=max(b.r2,a.r2+b.c1-b.c2); } }s[maxn*3<<2]; int num[maxn*3+10],tot; void update(int o,int l,int r,int p){ if(l==r)s[o].val(num[p]); else{ int m=l+r>>1; if(p<=m)update(o<<1,l,m,p); else update(o<<1|1,m+1,r,p); s[o].merge(s[o<<1],s[o<<1|1]); } } void build(int o,int l,int r){ if(l==r)s[o].val(num[l]); else{ int m=l+r>>1; build(o<<1,l,m); build(o<<1|1,m+1,r); s[o].merge(s[o<<1],s[o<<1|1]); } } struct EDGE{ int u,v,next; }edge[2*maxn+10]; int head[maxn+10],pp; void adde(int u,int v){ edge[++pp]=(EDGE){u,v,head[u]}; head[u]=pp; } int pos[maxn+10]; void dfs(int u,int fa){ num[++tot]=-1; pos[num[++tot]=u]=tot; for(int i=head[u];i;i=edge[i].next){ int v=edge[i].v; if(v!=fa)dfs(v,u); } num[++tot]=-2; } int main(){ int n; scanf("%d",&n); for(int i=1;i<=n;i++)vis[i]=1; for(int i=1;i<n;i++){ int u,v; scanf("%d%d",&u,&v); adde(u,v); adde(v,u); } dfs(1,0); build(1,1,tot); int q,cnt=n; scanf("%d",&q); char e[2]; while(q--){ scanf("%s",e); if(e[0]=='G'){ if(cnt==0)puts("-1"); else if(cnt==1)puts("0"); else printf("%d\n",s[1].dis); }else{ int u; scanf("%d",&u); cnt+=vis[u]=-vis[u]; update(1,1,tot,pos[u]); } } return 0; }