题面理解很容易,求树上两颗树节点数之和。
这里需要lct维护虚子树信息。
对于每个节点,我们记录认的儿子和不认的儿子大小之和(s),和不认的儿子大小和(sx)。
在access中,x节点换儿子时,需要对x的sx值加上前任儿子值,在减去现任儿子值。
代码:
#include<cstdio> #include<algorithm> using namespace std; #define N 100500 #define ll long long ll n,m; ll ch[N][2],fa[N],s[N],sx[N]; bool rt[N],res[N]; void update(ll x) { s[x] = sx[x]+s[ch[x][0]]+s[ch[x][1]]+1; } void reser(ll x) { res[x]^=1; swap(ch[x][0],ch[x][1]); } void pushdown(ll x) { if(res[x]) { res[x]=0; reser(ch[x][0]); reser(ch[x][1]); } } void down(ll x) { if(!rt[x])down(fa[x]); pushdown(x); } void rotate(ll x) { ll y = fa[x] , k = (ch[y][1]==x); if(rt[y])rt[y]=0,rt[x]=1; else ch[fa[y]][ch[fa[y]][1]==y] = x; fa[x] = fa[y]; ch[y][k] = ch[x][!k],fa[ch[x][!k]] = y; ch[x][!k] = y,fa[y] = x; update(y),update(x); } void splay(ll x) { down(x); while(!rt[x]) { ll y = fa[x], z = fa[y]; if(!rt[y]) ((ch[y][1]==x)^(ch[z][1]==y))?rotate(x):rotate(y); rotate(x); } } void access(ll x) { ll y = 0; while(x) { splay(x); sx[x]+=s[ch[x][1]]; sx[x]-=s[y]; rt[ch[x][1]] = 1,rt[y] = 0; ch[x][1] = y; update(x); y=x,x=fa[x]; } } void mtr(ll x) { access(x); splay(x); reser(x); } void link(ll x,ll y) { mtr(x); access(y); splay(y); fa[x] = y; sx[y]+=s[x]; update(y); } int main() { scanf("%lld%lld",&n,&m); for(ll i=1;i<=n;i++)rt[i]=1,s[i]=1; char cc[2]; ll u,v; for(ll i=1;i<=m;i++) { scanf("%s%lld%lld",cc,&u,&v); if(cc[0]=='Q') { mtr(u); access(v); splay(u); printf("%lld ",(s[u]-s[v])*(s[v])); }else link(u,v); } return 0; }