树链剖分都会吧?
维护一个二叉树的链剖,若size()相同选左子树。
每次删除一个叶子节点,若出现size()相等保持原有的剖法。
每次询问所有重边所指向的点的编号和。
n<=2*10^5
用链剖来维护链剖。(高级,用splay维护splay)
发现每一个点都必属于一条重链,于是就用总和-链顶和就是答案。
那么我们用线段树维护每一段区间的链顶和T,和每个点重儿子的size()-轻儿子的size()
C
每次操作都相当于把这个点到root上所有链顶的父亲的C+1,其余点的C-1.
具体操作可以先把所有点-1,再把所有链顶的父亲+2.
然后,对于所有C<0的点,把它的儿子的T值交换(必有一个为0,一个不为0)。
维护C的最小值就行了。
常数特别大,带3个log……
不过这道题的时限是3s
O(∩_∩)O~
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define N 200005
using namespace std;
typedef long long ll;
struct note{int c;ll t;}t[N*3];
int size[N],son[N],fa[N],l[N],r[N],d[N],w[N],top[N];
int n,m,root,x,tot,add[N*3],c[N*3];
bool bz[N];
ll sum;
void back(int v,int x) {t[v].c+=x;add[v]+=x;}
void down(int v) {
if (add[v]) {
back(v*2,add[v]);
back(v*2+1,add[v]);
add[v]=0;
}
}
note merge(note y,note z) {
note x;x.t=y.t+z.t;
x.c=min(y.c,z.c);
return x;
}
void dfs(int x) {
if (!x) return;size[x]=1;
dfs(l[x]);dfs(r[x]);size[x]+=size[l[x]]+size[r[x]];
d[l[x]]=d[r[x]]=d[x]+1;
if (size[r[x]]>size[l[x]]) son[x]=r[x];else son[x]=l[x];
}
void make(int x,int y) {
if (!x) return;
top[x]=y;w[x]=++tot;c[tot]=x;
if (!son[x]) return;
make(son[x],y);
if (l[x]!=son[x]) make(l[x],l[x]);
else make(r[x],r[x]);
}
void change(int v,int l,int r,int x,int y,int z) {
if (!x||!y||x>y) return;
if (l==x&&r==y) {back(v,z);return;}
int m=(l+r)/2;down(v);
if (y<=m) change(v*2,l,m,x,y,z);
else if (x>m) change(v*2+1,m+1,r,x,y,z);
else change(v*2,l,m,x,m,z),change(v*2+1,m+1,r,m+1,y,z);
t[v]=merge(t[v*2],t[v*2+1]);
}
void seek(int v,int l,int r) {
if (l==r) {change(1,1,n,w[fa[c[l]]],w[fa[c[l]]],2);return;}
int m=(l+r)/2;down(v);
if (t[v*2].t) seek(v*2,l,m);
if (t[v*2+1].t) seek(v*2+1,m+1,r);
t[v]=merge(t[v*2],t[v*2+1]);
}
void push(int v,int l,int r,int x,int y) {
if (l==x&&r==y) {seek(v,l,r);return;}
int m=(l+r)/2;down(v);
if (y<=m) {
if (t[v*2].t) push(v*2,l,m,x,y);
} else if (x>m) {
if (t[v*2+1].t) push(v*2+1,m+1,r,x,y);
} else {
if (t[v*2].t) push(v*2,l,m,x,m);
if (t[v*2+1].t) push(v*2+1,m+1,r,m+1,y);
}
t[v]=merge(t[v*2],t[v*2+1]);
}
void del(int x) {
int f=top[x];
while (f!=root) {
change(1,1,n,w[f],w[x],-1);push(1,1,n,w[f],w[x]);
x=fa[f];f=top[x];
}
change(1,1,n,w[f],w[x],-1);push(1,1,n,w[f],w[x]);
}
void revise(int v,int l,int r,int x) {
if (!x) return;
if (l==r) {
bz[c[l]]=t[v].t^=c[l];
return;
}
int m=(l+r)/2;down(v);
if (x<=m) revise(v*2,l,m,x);
else revise(v*2+1,m+1,r,x);
t[v]=merge(t[v*2],t[v*2+1]);
}
void per(int v,int a,int b) {
if (a==b) {
revise(1,1,n,w[l[c[a]]]);
revise(1,1,n,w[r[c[a]]]);
t[v].c*=-1;return;
}
int m=(a+b)/2;down(v);
if (t[v*2].c<0) per(v*2,a,m);
if (t[v*2+1].c<0) per(v*2+1,m+1,b);
t[v]=merge(t[v*2],t[v*2+1]);
}
void find(int v,int l,int r,int x,int y) {
if (l==x&&r==y) {per(v,l,r);return;}
int m=(l+r)/2;down(v);
if (y<=m) {
if (t[v*2].c<0) find(v*2,l,m,x,y);
} else if (x>m) {
if (t[v*2+1].c<0) find(v*2+1,m+1,r,x,y);
} else {
if (t[v*2].c<0) find(v*2,l,m,x,m);
if (t[v*2+1].c<0) find(v*2+1,m+1,r,m+1,y);
}
t[v]=merge(t[v*2],t[v*2+1]);
}
void rev(int x) {
int f=top[x];
while (f!=root) {
find(1,1,n,w[f],w[x]);
x=fa[f];f=top[x];
}
find(1,1,n,w[f],w[x]);
}
int main() {
freopen("heavy.in","r",stdin);
freopen("heavy.out","w",stdout);
scanf("%d",&n);sum=(ll)n*(n+1)/2;
fo(i,1,n) scanf("%d%d",&l[i],&r[i]),
fa[l[i]]=fa[r[i]]=i;
fo(i,1,n) if (!fa[i]) root=i;
dfs(root);make(root,root);
fo(i,1,n) {
if (son[i]==l[i]) x=size[l[i]]-size[r[i]];
else x=size[r[i]]-size[l[i]];
change(1,1,n,w[i],w[i],x);
if (top[i]==i) revise(1,1,n,w[i]);
}
printf("%lld\n",sum-t[1].t);
for(scanf("%d",&m);m;m--) {
scanf("%d",&x);
del(x);rev(x);
if (bz[x]) revise(1,1,n,w[x]);
if (x==l[fa[x]]) l[fa[x]]=0;
else r[fa[x]]=0;
fa[x]=0;sum-=x;
printf("%lld\n",sum-t[1].t);
}
}
嘿嘿,发扬码农精神,1h+打完A掉。