解決
エンドポイントを検索して、サブツリーのサイズを掛けました。
これは、エッジ両側から直接意図されている\(makeroot \)こと。答えは、ルートノードです\(サイズ\) 。
しかし、どのように維持するために、(サイズ\)\それを?実際のサブツリーのサイズは、二つの実の息子から直接入手することができます。しかし、サブツリー架空ではありません。各点複数維持することができる\(SV [] \) 、仮想ノードのサブツリーの大きさの合計を表します。
だから、\(腕立て伏せ\)に応じて次のようになります。
inline void pushup(int x){s[x]=s[ch[x][0]]+s[ch[x][1]]+sv[x]+1;}
我々はすでに、各ノードを知っていることを前提としてい\(SV [] \) 、アクションが変更されるかを検討し\(SV [] \) 。
実際の状況に関与したものは確か側スイッチングを操作しています。同様\(リンク\)、\ (アクセス\)カテゴリ。
それはこれに沸きます:
\(回転\)と\(スプレイが\) 、ノードの相対位置を変化させる実際の状況の側面に影響を与えない、チェーン上の実際の動作であり、修正する必要はありません\(SV [] \) 。
\(アクセス\)右の本当の息子は、右のノードを通過した後、元の仮想息子になるたびに、本当の息子になります。我々は変更する必要があるので\(SV [] \) 。
inline void access(int x){
for(int y=0;x;y=x,x=fa[x])
splay(x),sv[x]+=s[ch[x][1]]-s[y],ch[x][1]=y,pushup(x);
}
\(makeroot \)、\ (スプリット\)、\ (FindRootで\)だけで元の関数を呼び出します。変更を加えないでください。
\(リンク\)になり\(X \)となり\(Y \)仮想息子、変更\(Y \)\(SV [] \) 。そして、特別な注意ができるようにするだけでなく、(X \)\ルート位置に、\(\ y軸)は、ルートノードに配置する必要があります。場合\(yが\) 、ルートでない\(SV [Y] \) 、変更\(Y \)の祖先\(SVは[] \) 、これは許容できない、可能性のある変更することでもあります。
inline void link(int x,int y){
makeroot(x);makeroot(y);//如果y不在根节点,那它祖先的虚子树信息也要一起修改;
fa[x]=y;
sv[y]+=s[x];
}
\(カット\)だけで、本当のエッジを削除(押し上げは\)\、修正する気にしないでください。
コード
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define REP(i,a,b) for(int i=(a),ed=(b);i<=ed;++i)
inline int read(){
register int x=0,f=1;register char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=0;ch=getchar();}
while(isdigit(ch)){x=x*10+(ch^'0');ch=getchar();}
return f?x:-x;
}
const int N=1e5+10;
int n,q;
namespace LinkCutTree{
int fa[N],ch[N][2],r[N],s[N],sv[N];
inline void pushr(int x){swap(ch[x][0],ch[x][1]),r[x]^=1;}
inline void pushdown(int x){if(r[x])pushr(ch[x][0]),pushr(ch[x][1]),r[x]=0;}
inline void pushup(int x){s[x]=s[ch[x][0]]+s[ch[x][1]]+sv[x]+1;}
inline int getdir(int x){return x==ch[fa[x]][1];}
inline bool noroot(int x){return x==ch[fa[x]][0]||x==ch[fa[x]][1];}
inline void rotate(int x){
int f=fa[x],p=fa[f],k=getdir(x),s=ch[x][k^1];
ch[x][k^1]=f;ch[f][k]=s;if(noroot(f))ch[p][getdir(f)]=x;
fa[f]=x;fa[x]=p;if(s)fa[s]=f;
pushup(f);
}
inline void splay(int x){
static int stk[N];
int tp=0,y=x;stk[++tp]=y;
while(noroot(y))stk[++tp]=y=fa[y];
while(tp)pushdown(stk[tp--]);
for(int f=fa[x];noroot(x);rotate(x),f=fa[x])
if(noroot(f))rotate(getdir(f)==getdir(x)?f:x);
pushup(x);
}
inline void access(int x){
for(int y=0;x;y=x,x=fa[x])
splay(x),sv[x]+=s[ch[x][1]]-s[y],ch[x][1]=y,pushup(x);
}
inline void makeroot(int x){
access(x);splay(x);pushr(x);
}
inline void split(int x,int y){
makeroot(x);access(y);splay(y);
}
inline void link(int x,int y){
makeroot(x);makeroot(y);//如果y不在根节点,那它祖先的虚子树信息也要一起修改;
fa[x]=y;
sv[y]+=s[x];
}
inline void cut(int x,int y){
split(x,y);
fa[x]=ch[y][0]=0;
}
}using namespace LinkCutTree;
int main(){
n=read(),q=read();
REP(t,1,q){
char ch;scanf("%c",&ch);
int x=read(),y=read();
if(ch=='Q'){
cut(x,y);makeroot(x);makeroot(y);
printf("%lld\n",1ll*s[x]*s[y]);
}
link(x,y);
}
return 0;
}