Luogu P4219 [BJOI2014]大融合

版权声明:转载注明出处,部分带(坑)文章谢绝转载。 https://blog.csdn.net/linjiayang2016/article/details/87277354

题目大意

维护一个动态森林,定义一条边的负载就是它所在的联通块上经过它的简单路径的数量,有两种操作。

  1. A   x   y A\ x\ y 表示在 x x y y 之间连一条边。保证之前 x x y y 是不联通的。
  2. Q   x   y Q\ x\ y 表示询问 ( x , y ) (x,y) 这条边上的负载。保证 x x y y 之间有一条边。

题解

题解有两种做做法,LCT和并查集+线段树合并。

LCT

首先,一眼看过去,如果不考虑代码复杂度,则渐进时间复杂度最低的LCT为 Θ ( n log n ) \Theta(n\log n) ,但注意到,本题需要求子树大小,而非路径长度,因此需要对原本的LCT做修改。

n o d e node

增加一个成员 v s i z e vsize ,表示所有轻链(非重链)上的节点个数。
s i z e size 的定义修改成整棵子树的大小,这也意味着如果仍要保留路径总和,需要再多一个成员。

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)
};
u p d a t e   &   p u s h d o w n update\ \&\ pushdown

注意在 u p d a t e update s i z e size 要加上 v s i z e vsize

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]);
}
R o t a t e   &   S p l a y Rotate\ \&\ Splay

这里和普通LCT没有区别。

a c c e s s access

由于轻重关系变了,因此当前节点的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);
	}
}
m a k e _ r o o t make\_root

依赖 r e v s revs a c c e s s access ,由于那两个函数均处理完毕,这里和普通LCT没有区别。

l i n k link

由于多了一条轻边,因此 v s i z e vsize 要加上轻儿子的大小。

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;
}
c u t cut

由于断边之前会 make_root(x),access(y),即让两个节点在一条重链上,因此不涉及到轻边,不需要做任何改动, u p d a t e update 可以完成处理。

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;
}

猜你喜欢

转载自blog.csdn.net/linjiayang2016/article/details/87277354
今日推荐