传送门
题解:对每个点建权值线段树(权值即点在DFS序列中的编号),合并的时候直接合并两个点根的线段树,并连一下并查集,查询的时候找到x,y所在树的根f,假设dep[x]>dep[y],那答案就是(size[f]-size[x])*size[x](这一点的解释尽快补上,不过好像不比较也能过,难道是数据水?)。
P.S.在询问时要选深度更深的点的原因:
如果dep[x] < dep[y]并且选择了x,那么x的子树就包括了y点,而答案是两边子树大小的乘积,所以这样会造成多余贡献,不合法。
#include
using namespace std;
const int MAXN=1e5+4;
int n,m;
int head[MAXN],fa[MAXN],edge=0;
int dep[MAXN],in[MAXN],out[MAXN],tim=0;
bool vis[MAXN];
struct EDGE {
int v,nxt;
}e[MAXN<<1];
struct Q {
int opt,x,y;
}q[MAXN];
int siz[MAXN*18],lc[MAXN*18],rc[MAXN*18],root[MAXN],tot=0;
int find(int x) {
return fa[x]==x?x:fa[x]=find(fa[x]);
}
inline void adde(int u,int v) {
e[++edge].nxt=head[u],e[edge].v=v,head[u]=edge;
e[++edge].nxt=head[v],e[edge].v=u,head[v]=edge;
}
inline int read() {
int x=0;char c=getchar();
while (c<'0'||c>'9') c=getchar();
while (c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
return x;
}
inline void pushup(int rt) {
siz[rt]=siz[lc[rt]]+siz[rc[rt]];
}
void insert(int &rt,int l,int r,int val) {
rt=++tot;
if (l==r) {siz[rt]=1;return ;}
int mid=(l+r)>>1;
if (val<=mid) insert(lc[rt],l,mid,val);
else insert(rc[rt],mid+1,r,val);
pushup(rt);
}
inline int merge(int a,int b) {
if (!a) return b;
if (!b) return a;
lc[a]=merge(lc[a],lc[b]);
rc[a]=merge(rc[a],rc[b]);
pushup(a);
return a;
}
int query(int rt,int l,int r,int L,int R) {
if (L<=l&&r<=R) return siz[rt];
int mid=(l+r)>>1,ret=0;
if (L<=mid) ret+=query(lc[rt],l,mid,L,R);
if (mid1,r,L,R);
return ret;
}
void dfs(int p,int fa) {
in[p]=++tim,vis[p]=true;
insert(root[p],1,n,in[p]);
for (int i=head[p];~i;i=e[i].nxt) {
int v=e[i].v;
if (v^fa)
dep[v]=dep[p]+1,dfs(v,p);
}
out[p]=tim;
}
int main() {
// freopen("bzoj 4530.in","r",stdin);
memset(head,-1,sizeof(head));
memset(vis,false,sizeof(vis));
n=read(),m=read();
for (register int i=1;i<=m;++i) {
char ss;
while (!isalpha(ss=getchar()));
q[i].opt=(ss^'Q')?1:2;
q[i].x=read(),q[i].y=read();
if (q[i].opt&1) adde(q[i].x,q[i].y);
}
for (register int i=1;i<=n;++i){
fa[i]=i;
if (!vis[i]) dep[i]=0,dfs(i,0);
}
/* for (int i=1;i<=n;++i)
cout<' ';
cout<*/
for (register int i=1;i<=m;++i) {
if (q[i].opt&1) {
int x=q[i].x,y=q[i].y;
x=find(x),y=find(y);
if (dep[x]>dep[y]) x^=y^=x^=y;//find the earlier-linked node(new node link to old node)
root[x]=merge(root[x],root[y]);
fa[y]=x;
}
else {
int x=q[i].x,y=q[i].y;
int pos=(dep[x]y])?y:x;//find the later-linked node to avoid repeated calculation
int f=find(pos);
int temp=query(root[f],1,n,in[pos],out[pos]);
printf("%lld\n",1ll*(siz[root[f]]-temp)*temp);
}
}
return 0;
}