题意:
给出nnn个独立的点,处理以下qqq个操作,每个操作只会是如下操作之一
(1)连接x,yx,yx,y,保证原本没有边存在
(2)查询边(x,y)(x,y)(x,y)的负荷,定义负荷为树上经过(x,y)(x,y)(x,y)的简单路径的个数
LCTLCTLCT维护(原树中)子树信息
设sizesizesize是当前结点为根的情况下的子树信息和,vsize(virtual_size)vsize(virtual\_size)vsize(virtual_size)是当前结点为根的情况下的所有虚儿子的子树和。
如果给定根,即已知xxx的父节点fafafa,那么只需要split(x,fa)split(x,fa)split(x,fa),xxx的vsizevsizevsize就已经是原树的子树信息和了。
更一般的,sizesizesize维护的是原树这个图的连接的所有连通分量的信息和,如果给定根要找出子树信息和,只需要剥离他的父亲即可。
另外,在维护子树信息时,link(x,y)link(x,y)link(x,y)时,最好先splay(y)splay(y)splay(y)。因为yyy也有父亲,给yyy连接了虚儿子后,也需要push_up ypush\_up\ ypush_up y的父亲,先splay(y)splay(y)splay(y)后,yyy就没有父亲了,也就不需要更新。
也可以在连接虚儿子后splay(y)splay(y)splay(y),此时的含义就是手动更新yyy的祖先们。
维护子树信息只需要在更改了虚实关系的函数更新vsizevsizevsize即可,仅需在link,accesslink,accesslink,access中更新
#include<bits/stdc++.h>
#define ll long long
using namespace std;
struct LCT
{
struct node
{
int v,tag,son[2],fa,size,vsize;
};
stack<int>s;
vector<node>tree;
LCT():tree(100005){}
bool isroot(int x)
{
return tree[tree[x].fa].son[0]!=x&&tree[tree[x].fa].son[1]!=x;
}
int getson(int x,int y)
{
return x==tree[y].son[1];
}
void push_up(int x)
{
tree[x].size=1+tree[x].vsize+tree[tree[x].son[0]].size+tree[tree[x].son[1]].size;
}
void rotate(int x)
{
int y=tree[x].fa,z=tree[y].fa;
int k1=getson(x,y),k2=getson(y,z);
tree[x].fa=z;
if(!isroot(y)) tree[z].son[k2]=x;
tree[y].son[k1]=tree[x].son[!k1];
if(tree[x].son[!k1]) tree[tree[x].son[!k1]].fa=y;
tree[x].son[!k1]=y;
tree[y].fa=x;
push_up(y); push_up(x);
}
void reverse(int x)
{
swap(tree[x].son[0],tree[x].son[1]);
tree[x].tag^=1;
}
void push_down(int x)
{
if(!tree[x].tag) return;
for(int i=0;i<=1;i++)
if(tree[x].son[i]) reverse(tree[x].son[i]);
tree[x].tag=0;
}
void splay(int x)
{
int now=x;
push_up(now);
s.push(now);
while(!isroot(now))
{
s.push(tree[now].fa);
// push_up(tree[now].fa);
now=tree[now].fa;
}
while(!s.empty())
{
push_down(s.top());
s.pop();
}
while(!isroot(x))
{
int y=tree[x].fa,z=tree[y].fa;
int k1=getson(x,y),k2=getson(y,z);
if(!isroot(y))
{
if(k1==k2) rotate(y);
else rotate(x);
}
rotate(x);
}
}
void access(int x)//打通root->x的splay
{
int last=0;
while(x)
{
splay(x);
tree[x].vsize+=tree[tree[x].son[1]].size-tree[last].size;
tree[x].son[1]=last;
push_up(last=x);
x=tree[x].fa;
}
}
void makeroot(int x)
{
access(x);
splay(x);
reverse(x);
}
int findroot(int x)
{
access(x);
splay(x);
while(tree[x].son[0])
{
push_down(x);
x=tree[x].son[0];
}
splay(x);
return x;
}
void split(int x,int y)
{
makeroot(x);
access(y);
splay(y);
}
void link(int x,int y)
{
makeroot(x);
if(x==findroot(y)) return;
splay(y);
tree[x].fa=y;
tree[y].vsize+=tree[x].size;
push_up(y);
}
// void cut(int x,int y)
// {
// makeroot(x);
// if(findroot(y)!=x||tree[y].fa!=x||tree[y].son[0]) return;
// tree[y].fa=tree[x].son[1]=0;
// }
}lct;
int n,q;
int main()
{
scanf("%d%d",&n,&q);
while(q--)
{
char s[5]; int x,y;
scanf("%s%d%d",s+1,&x,&y);
if(s[1]=='A') lct.link(x,y);
else
{
lct.split(x,y);
printf("%lld\n",1ll*(lct.tree[x].vsize+1)*(lct.tree[y].vsize+1));
}
}
return 0;
}
本文介绍了一个名为LCT的数据结构,用于高效处理树的连接、查询边的负荷和子树信息维护。核心操作包括link、split和access,展示了如何通过旋转、splay等技巧优化树的结构,以支持复杂查询。
616

被折叠的 条评论
为什么被折叠?



