版权声明:转载注明出处,部分带(坑)文章谢绝转载。 https://blog.csdn.net/linjiayang2016/article/details/87277354
题目大意
维护一个动态森林,定义一条边的负载就是它所在的联通块上经过它的简单路径的数量,有两种操作。
- 表示在 和 之间连一条边。保证之前 和 是不联通的。
- 表示询问 这条边上的负载。保证 和 之间有一条边。
题解
题解有两种做做法,LCT和并查集+线段树合并。
LCT
首先,一眼看过去,如果不考虑代码复杂度,则渐进时间复杂度最低的LCT为 ,但注意到,本题需要求子树大小,而非路径长度,因此需要对原本的LCT做修改。
增加一个成员
,表示所有轻链(非重链)上的节点个数。
的定义修改成整棵子树的大小,这也意味着如果仍要保留路径总和,需要再多一个成员。
struct node {
int size,rev,vsize;
node *fa,*ch[2];
node(node *f=NULL):fa(f),size(1),rev(0),vsize(0){ch[0]=ch[1]=NULL;}
#define size(t) (t?t->size:0)
#define vsize(t) (t?t->vsize:0)
#define rev(t) t&&(t->rev^=1)
};
注意在 时 要加上 。
void update() {
size=1+size(ch[0])+size(ch[1])+vsize;
}
void pushdown() {
if(!rev) return;
rev=0,swap(ch[0],ch[1]);
rev(ch[0]),rev(ch[1]);
}
这里和普通LCT没有区别。
由于轻重关系变了,因此当前节点的vsize要加上丢掉的重儿子,减去新加上的非重儿子。
void access(node *p) {
for(node *t=NULL;p;p->update(),t=p,p=p->fa){
splay(p),p->vsize+=size(p->ch[1]);
p->ch[1]=t;
p->vsize-=size(t);
}
}
依赖 和 ,由于那两个函数均处理完毕,这里和普通LCT没有区别。
由于多了一条轻边,因此 要加上轻儿子的大小。
void link(node *x,node *y) {
if(!x||!y||rooter(x)==rooter(y))
return ;
make_root(x);
make_root(y);
x->fa=y;
y->vsize+=x->size;
}
由于断边之前会 make_root(x),access(y)
,即让两个节点在一条重链上,因此不涉及到轻边,不需要做任何改动,
可以完成处理。
LCT完整代码
// luogu-judger-enable-o2
#include<bits/stdc++.h>
using namespace std;
const int maxn=200005;
struct node {
int size,rev,vsize;
node *fa,*ch[2];
node(node *f=NULL):fa(f),size(1),rev(0),vsize(0){
ch[0]=ch[1]=NULL;
}
#define size(t) (t?t->size:0)
#define vsize(t) (t?t->vsize:0)
#define rev(t) t&&(t->rev^=1)//test?
#define sum(t) (t?t->sum:0)
void update() {
size=1+size(ch[0])+size(ch[1])+vsize;
}
void pushdown() {
if(!rev) return;
rev=0,swap(ch[0],ch[1]);
rev(ch[0]),rev(ch[1]);
}
};
struct LCT {
#define which(p) (p->fa->ch[1]==p)
#define is_root(p) ((!p->fa)||(p->fa->ch[0]!=p&&p->fa->ch[1]!=p))
node *pt[maxn];
void init(int n) {
for(int i=1; i<=n; i++)
pt[i]=new node();
}
void rotate(node *p) {
int a=which(p)^1;
node *f=p->fa;
f->ch[a^1]=p->ch[a];
if(p->ch[a]) p->ch[a]->fa=f;
p->fa=f->fa;
if(!is_root(f)) p->fa->ch[which(f)]=p;
f->fa=p,p->ch[a]=f;
f->update(),p->update();
}
void splay(node *p) {
static node *tmp[maxn];
int pos=0;
for(node *t=p;; t=t->fa) {
tmp[++pos]=t;
if(is_root(t)) break;
}
for(; pos; --pos)
tmp[pos]->pushdown();
for(; !is_root(p); rotate(p))
if(!is_root(p->fa))
rotate(which(p)==which(p->fa)?p->fa:p);
}
void access(node *p) {
for(node *t=NULL;p;p->update(),t=p,p=p->fa){
splay(p),p->vsize+=size(p->ch[1]);
p->ch[1]=t;
p->vsize-=size(t);
}
}
void make_root(node *p) {
access(p),splay(p),rev(p);
}
node *rooter(node *x){
access(x);
splay(x);
while(x->pushdown(),x->ch[0])
x=x->ch[0];
return x;
}
void link(node *x,node *y) {
if(!x||!y||rooter(x)==rooter(y))
return ;
make_root(x);
make_root(y);
x->fa=y;
y->vsize+=x->size;
}
void cut(node *x,node *y) {
make_root(x),access(y),splay(y);
if(y->ch[0]==x)
x->fa=y->ch[0]=NULL;
y->update();
x->update();
}
void link(int a,int b){
link(pt[a],pt[b]);
}
void cut(int a,int b){
cut(pt[a],pt[b]);
}
long long query(int,int);
} lct;
int n,m,u,v;
char s[110];
long long LCT::query(int a,int b){
node *x=pt[a],*y=pt[b];
cut(x,y);
make_root(x);
make_root(y);
long long ans=(long long)size(x)*size(y);
link(x,y);
return ans;
}
int main() {
scanf("%d%d",&n,&m);
lct.init(n+1);
while(m--){
scanf("%s%d%d",s,&u,&v);
if(s[0]=='A')
lct.link(u,v);
else printf("%lld\n",lct.query(u,v));
}
return 0;
}