P9620 歌姬 题解
这里介绍一种不用考虑虚树根位置的方法。
首先求出所有节点子树内被选出的节点个数
然后就可以发现虚树根位置就是
可以发现选出
然后就可以发现
于是直接线段树维护最大和严格第二大的值就好了。
#include<iostream>
#include<cstdio>
using namespace std;
#define N 1000010
#define l(p) p<<1
#define r(p) p<<1|1
struct edge{int next,to;}e[N];char s[10];bool usd[N];
int head[N],ip[N],st[N],ed[N],n,m,cnt,res;
int lt[N],pt[N],lz[N],id[N],idx,ipx;
struct sd{
int a,b;
void operator +=(const int &x){a+=x;if(~b)b+=x;}
sd operator +(const sd &x){
if(x.a>a)return (sd){x.a,max(x.b,a)};
else if(x.a<a)return (sd){a,max(x.a,b)};
else return (sd){x.a,max(x.b,b)};
}
}mx[N];
void add(int a,int b){
e[++cnt].to=b;
e[cnt].next=head[a];
head[a]=cnt;
}
void down(int p){
mx[l(p)]+=lz[p],lz[l(p)]+=lz[p];
mx[r(p)]+=lz[p],lz[r(p)]+=lz[p];
}
void app(int p,int l,int r,int i,int j,int k){
if(i<=l&&r<=j){mx[p]+=k,lz[p]+=k;return;}
if(lz[p])down(p),lz[p]=0;
if(i<=((l+r)>>1))app(l(p),l,(l+r)>>1,i,j,k);
if(j>((l+r)>>1))app(r(p),((l+r)>>1)+1,r,i,j,k);
mx[p]=mx[l(p)]+mx[r(p)];
}
void dfs(int p,int f){
ip[p]=1,pt[p]=f;
for(int i=head[p];i;i=e[i].next){
if(e[i].to==f)continue;
dfs(e[i].to,p),ip[p]+=ip[e[i].to];
if(ip[lt[p]]<ip[e[i].to])lt[p]=e[i].to;
}
}
void cfs(int p,int f){
if(lt[f]==p)ed[id[p]=id[f]]=p;
else st[id[p]=++idx]=p;ip[p]=++ipx;
if(lt[p])cfs(lt[p],p);
for(int i=head[p];i;i=e[i].next)
if(e[i].to!=f&&e[i].to!=lt[p])cfs(e[i].to,p);
}
void mak(int p,int k){
for(;id[p]!=1;p=pt[st[id[p]]])
app(1,1,n,ip[st[id[p]]],ip[p],k);
app(1,1,n,1,ip[p],k),res+=k;
}
int main(){
scanf("%d%d",&n,&m);for(int i=1;i<=n*4;i++)mx[i].b=-1;
for(int i=1,a,b;i<n;i++)scanf("%d%d",&a,&b),add(a,b),add(b,a);
dfs(1,0),cfs(1,0);
for(int i=1,a;i<=m;i++){
scanf("%s%d",s,&a);
if(s[0]=='R'&&!usd[a])usd[a]=true,mak(a,1);
else if(s[0]=='W'&&usd[a])usd[a]=false,mak(a,-1);
printf("%d\n",res-mx[1].b);
}
return 0;
}