题目描述:
小强要在 $N$ 个孤立的星球上建立起一套通信系统。这套通信系统就是连接 $N$ 个点的一个树。这个树的边是一条一条添加上去的。在某个时刻,一条边的负载就是它所在的当前能够联通的树上路过它的简单路径的数量。
例如,在上图中,现在一共有五条边。其中,$(3,8)$ 这条边的负载是 $6$,因为有六条简单路径 $2-3-8, 2-3-8-7, 3-8, 3-8-7, 4-3-8, 4-3-8-7$ 路过了 $(3,8)$。
现在,你的任务就是随着边的添加,动态的回答小强对于某些边的负载的询问。
输入:
第一行包含两个整数 $N,Q$,表示星球的数量和操作的数量。星球从 $1$ 开始编号。
接下来的 $Q$ 行,每行是如下两种格式之一:
``A x y`` 表示在 $x$ 和 $y$ 之间连一条边。保证之前 $x$ 和 $y$ 是不联通的。
``Q x y`` 表示询问 $(x,y)$ 这条边上的负载。保证 $x$ 和 $y$ 之间有一条边。
输出:
对每个查询操作,输出被查询的边的负载。
数据范围:
对于所有数据,$1 leq N,Q leq 100000$。
算法标签:LCT
思路:
可以考虑LCT维护整棵树的情况,维护子树大小情况。
以下代码:
#include<bits/stdc++.h> #define il inline #define LL long long #define _(d) while(d(isdigit(ch=getchar()))) using namespace std; const int N=1e5+5; int n,q,sz[N],v[N]; il int read(){ int x,f=1;char ch; _(!)ch=='-'?f=-1:f;x=ch^48; _()x=(x<<1)+(x<<3)+(ch^48); return f*x; } class LCT{ int son[N][2],fa[N],rev[N],sta[N]; il bool isroot(int x){ return (son[fa[x]][0]!=x&&son[fa[x]][1]!=x); } il void update(int x){ sz[x]=sz[son[x][0]]+sz[son[x][1]]+v[x]; } il void pushdown(int x){ if(!rev[x])return; swap(son[x][0],son[x][1]);rev[x]=0; rev[son[x][0]]^=1;rev[son[x][1]]^=1; } il void rotate(int x){ int y=fa[x],z=fa[y],tp=(son[y][1]==x); if(!isroot(y))son[z][son[z][1]==y]=x; son[y][tp]=son[x][tp^1];fa[son[y][tp]]=y; son[x][tp^1]=y;fa[y]=x;fa[x]=z; update(y); } il void splay(int x){ for(update(x);!isroot(x);rotate(x)){ int y=fa[x],z=fa[y]; if(!isroot(y))rotate(((son[y][1]==x)^(son[z][1]==y))?x:y); } update(x); } il void access(int x){ for(int y=0;x;y=x,x=fa[x]){ splay(x);v[x]+=sz[son[x][1]]-sz[y]; son[x][1]=y;update(x); } } il void makeroot(int x){ access(x);splay(x);rev[x]^=1; } public: il void link(int x,int y){ makeroot(x);makeroot(y);fa[y]=x; sz[x]+=sz[y];v[x]+=sz[y]; } il LL query(int x,int y){ makeroot(x);access(y);splay(y); return 1ll*sz[x]*(sz[y]-sz[x]); } }T; int main() { n=read();q=read(); for(int i=1;i<=n;i++)v[i]=sz[i]=1; while(q--){ char ch;scanf(" %c",&ch); int x=read(),y=read(); if(ch=='Q')printf("%lld ",T.query(x,y)); else T.link(x,y); } return 0; }