传送门
题意:
给一个动态树,维护sze集合大小。
题解:
动态树。
LCT维护子树和的方法(不支持子树修改,其实实现起来也挺简单的):
对于维护这一类满足加减的信息,可以考虑在 LCT 的每个点分别维护出所有虚边和实边连向他和。
如果得到了维护,那么可以轻松在 O(logn) 时间内完成询问。
考虑怎么维护:
首先 Splay 不会改变虚边的性质,那么 Splay 中直接动态调整实边的和就好了。
对于 Access 操作,显然断开右儿子会导致虚实边切换,可以在维护的东西上简单加减就好了。不过需要注意的是,加减最好在当前点是其所在 Splay 的根节点的时候才能加减,否则该点的和改变后其父节点的和也会相应改变(只有实父亲),还要向上 update (虚父亲不用更新是因为 Access 操作会一路向上走,总会更新到虚父亲的)。
对于 Link 操作,直接连边也会导致一样的错误,那么先将两边的点都置为根节点就好了。
对于每组查询操作,先将查询点 Access ,此时其虚子树和包含了其所有子树,可以加上自己的信息而得到答案。
关于维护最大最小值也可以用类似的方法。不过需要注意的是不能简单的加减,容易想到增加一个 logn 的时间复杂度,对每个点维护multiset来完成删除操作。
#include
using namespace std;
inline int rd(){
char ch=getchar();int i=0,f=1;
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){i=(i<<1)+(i<<3)+ch-'0';ch=getchar();}
return i*f;
}
inline void W(long long x){
static int buf[50];
if(!x){putchar('0');return;}
if(x<0){putchar('-');x=-x;}
while(x){buf[++buf[0]]=x%10,x/=10;}
while(buf[0]){putchar(buf[buf[0]--]+'0');}
}
const int Maxn=1e5+50;
int n,m;
struct node{
node *lc,*rc,*fa;
int sze_v,sze_t,revtag;
node():lc(NULL),rc(NULL),fa(NULL),sze_t(1){}
inline void rev();inline void upt();inline void pd();
}Pool[Maxn],*pool=Pool;
inline void node::upt(){sze_t=(lc?lc->sze_t+lc->sze_v:0)+(rc?rc->sze_t+rc->sze_v:0)+1;}
inline void node::rev(){revtag^=1;swap(lc,rc);}
inline bool isroot(node *x){return !x->fa||(x->fa->lc!=x&&x->fa->rc!=x);}
inline bool which(node *x){return x->fa->lc==x;}
inline void node::pd(){
if(!isroot(this))fa->pd();
if(revtag){
if(lc)lc->rev();if(rc)rc->rev();
revtag=0;
}
}
inline void rotate(node *x){
node *y=x->fa,*z=y->fa;
if(!isroot(y))((z->lc==y)?z->lc:z->rc)=x;
x->fa=z;y->fa=x;
if(y->lc==x){
node *b=x->rc;
x->rc=y;y->lc=b;
if(b)b->fa=y;
}else{
node *b=x->lc;
x->lc=y;y->rc=b;
if(b)b->fa=y;
}
y->upt();x->upt();
}
inline void splay(node *x){
x->pd();
while(!isroot(x)){
node *y=x->fa;
if(!isroot(y)){
(which(y)^which(x))?(rotate(x)):(rotate(y));
}rotate(x);
}
}
inline void access(node *x){
for(node *t=NULL;x;t=x,x=x->fa){
splay(x);int rc_sze=(x->rc)?(x->rc->sze_t+x->rc->sze_v):0;
x->sze_t-=rc_sze;x->sze_v+=rc_sze;
x->rc=t;rc_sze=(x->rc)?(x->rc->sze_t+x->rc->sze_v):0;
x->sze_t+=rc_sze;x->sze_v-=rc_sze;
}
}
inline void makeroot(node *x){
access(x);splay(x);x->rev();
}
inline void link(node *x,node *y){
makeroot(x);makeroot(y);x->fa=y;
y->sze_v+=(x->sze_v+x->sze_t);
}
inline void query(node *x,node *y){
makeroot(x);int sze1=x->sze_t+x->sze_v;
access(y);int sze2=y->sze_v+1;
W(1ll*(sze1-sze2)*sze2);putchar('\n');
}
int main(){
n=rd(),m=rd();
for(int i=1;i<=m;i++){
static char ch[2];
scanf("%s",ch+1);
(ch[1]=='A')?(link(pool+rd(),pool+rd())):(query(pool+rd(),pool+rd()));
}
}
线段树合并一样可以处理,考虑先把原树的关系处理出来,对于当前联通快,按照 dfs 序查询两颗子树就好了。
#include
using namespace std;
struct IO
{
streambuf *ib,*ob;
int buf[50];
inline void init()
{
ios::sync_with_stdio(false);
cin.tie(NULL);cout.tie(NULL);
ib=cin.rdbuf();ob=cout.rdbuf();
}
inline int read()
{
char ch=ib->sbumpc();int i=0,f=1;
while(!isdigit(ch)){if(ch=='-')f=-1;ch=ib->sbumpc();}
while(isdigit(ch)){i=(i<<1)+(i<<3)+ch-'0';ch=ib->sbumpc();}
return i*f;
}
inline void W(long long x)
{
if(!x){ob->sputc('0');ob->sputc('\n');return;}
if(x<0){ob->sputc('-');x=-x;}
while(x){buf[++buf[0]]=x%10;x/=10;}
while(buf[0]){ob->sputc(buf[buf[0]--]+'0');}
ob->sputc('\n');
}
}io;
const int Maxn=1e5+50;
int n,m,ecnt,rt[Maxn],dfn[Maxn],dep[Maxn],anc[Maxn],ind,out[Maxn],tot;
int sze[Maxn*20],lc[Maxn*20],rc[Maxn*20];
struct Q
{
int k,x,y;
}q[Maxn];
vector<int>edge[Maxn];
inline void dfs(int now,int f)
{
dfn[now]=++ind;dep[now]=dep[f]+1;
for(int e=edge[now].size()-1;e>=0;e--)
{
int v=edge[now][e];
if(v==f)continue;
dfs(v,now);
}
out[now]=ind;
}
inline void update(int now)
{
sze[now]=sze[lc[now]]+sze[rc[now]];
}
inline int getf(int now)
{
return (anc[now]==now)?(now):(anc[now]=getf(anc[now]));
}
inline void insert(int now,int l,int r,int pos)
{
if(l==r)
{
sze[now]=1;
return;
}
int mid=(l+r)>>1;
if(pos<=mid)insert(lc[now]=++tot,l,mid,pos);
else insert(rc[now]=++tot,mid+1,r,pos);
update(now);
}
inline int merge(int x,int y)
{
if(!y)return x;
if(!x)return y;
sze[x]+=sze[y];
lc[x]=merge(lc[x],lc[y]);
rc[x]=merge(rc[x],rc[y]);
return x;
}
inline int query(int now,int l,int r,int L,int R)
{
if(L<=l&&r<=R)return sze[now];
int mid=(l+r)>>1;
int res=0;
if(L<=mid&&lc[now])res+=query(lc[now],l,mid,L,R);
if(R>mid&&rc[now])res+=query(rc[now],mid+1,r,L,R);
return res;
}
int main()
{
io.init();n=io.read(),m=io.read();
for(int i=1;i<=n;i++)anc[i]=i;
for(int i=1;i<=m;i++)
{
static char ch[3];
cin>>(ch+1);
q[i].k=((ch[1]=='A')?1:2),q[i].x=io.read(),q[i].y=io.read();
if(q[i].k==1)
{
edge[q[i].x].push_back(q[i].y);
edge[q[i].y].push_back(q[i].x);
}
}
for(int i=1;i<=n;i++)if(!dfn[i])dfs(i,0);
for(int i=1;i<=n;i++)
{
rt[i]=++tot;
insert(rt[i],1,n,dfn[i]);
}
for(int i=1;i<=m;i++)
{
if(dep[q[i].x]>dep[q[i].y])swap(q[i].x,q[i].y);
if(q[i].k==1)
{
rt[getf(q[i].x)]=merge(rt[getf(q[i].x)],rt[getf(q[i].y)]);
anc[getf(q[i].y)]=anc[q[i].x];
}
else
{
int k=getf(q[i].y);
int v=query(rt[k],1,n,dfn[q[i].y],out[q[i].y]);
io.W(1ll*(sze[rt[k]]-v)*v);
}
}
}