zoukankan      html  css  js  c++  java
  • Luogu4219 [BJOI2014]大融合

    Link

    Solution

    求两端点的子树大小然后相乘。

    可以想到直接断边然后两边都(makeroot)一下。答案就是根节点的(size)

    但是怎么维护(size)呢?实子树大小可以直接由两个实儿子得到。但是虚子树不行。所以可以对每个点多维护一个(sv[]),表示这个节点的虚子树大小总和。

    那么(pushup)就相应变成:

    inline void pushup(int x){s[x]=s[ch[x][0]]+s[ch[x][1]]+sv[x]+1;}
    

    我们假设已经知道每个节点的(sv[]),考虑有哪些操作会改变(sv[])

    肯定是那些涉及虚实边切换的操作。像是(link)(access)一类。

    总结一下就是:

    (rotate)(splay)都是在实链上操作,只会改变节点的相对位置,并不影响虚实边,所以不需要修改(sv[])

    (access)每次会把原来的右实儿子变成虚儿子,再把传上来的节点变成右实儿子。所以需要更改(sv[])

    inline void access(int x){
    		for(int y=0;x;y=x,x=fa[x])
    			splay(x),sv[x]+=s[ch[x][1]]-s[y],ch[x][1]=y,pushup(x);
    	}
    

    (makeroot)(split)(findroot)都只是调用了原来的函数。不用做修改。

    (link)使(x)成为了(y)的虚儿子,需要修改(y)(sv[])。而且特别注意不仅要让(x)在根节点位置,(y)也要放到根节点处。如果(y)不在根节点,那么(sv[y])变化,(y)的祖先的(sv[])也有可能要随之变化,这是不能接受的。

    inline void link(int x,int y){
    		makeroot(x);makeroot(y);//如果y不在根节点,那它祖先的虚子树信息也要一起修改;
    		fa[x]=y;
    		sv[y]+=s[x];
    	}
    

    (cut)只是删除了一条实边,(pushup)会自行修改,不用管。

    Code

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    #define REP(i,a,b) for(int i=(a),ed=(b);i<=ed;++i)
    inline int read(){
    	register int x=0,f=1;register char ch=getchar();
    	while(!isdigit(ch)){if(ch=='-')f=0;ch=getchar();}
    	while(isdigit(ch)){x=x*10+(ch^'0');ch=getchar();}
    	return f?x:-x;
    }
    
    const int N=1e5+10;
    int n,q;
    
    namespace LinkCutTree{
    	int fa[N],ch[N][2],r[N],s[N],sv[N];
    	inline void pushr(int x){swap(ch[x][0],ch[x][1]),r[x]^=1;}
    	inline void pushdown(int x){if(r[x])pushr(ch[x][0]),pushr(ch[x][1]),r[x]=0;}
    	inline void pushup(int x){s[x]=s[ch[x][0]]+s[ch[x][1]]+sv[x]+1;}
    	inline int getdir(int x){return x==ch[fa[x]][1];}
    	inline bool noroot(int x){return x==ch[fa[x]][0]||x==ch[fa[x]][1];}
    	inline void rotate(int x){
    		int f=fa[x],p=fa[f],k=getdir(x),s=ch[x][k^1];
    		ch[x][k^1]=f;ch[f][k]=s;if(noroot(f))ch[p][getdir(f)]=x;
    		fa[f]=x;fa[x]=p;if(s)fa[s]=f;
    		pushup(f);
    	}
    	inline void splay(int x){
    		static int stk[N];
    		int tp=0,y=x;stk[++tp]=y;
    		while(noroot(y))stk[++tp]=y=fa[y];
    		while(tp)pushdown(stk[tp--]);
    		for(int f=fa[x];noroot(x);rotate(x),f=fa[x])
    			if(noroot(f))rotate(getdir(f)==getdir(x)?f:x);
    		pushup(x);
    	}
    	inline void access(int x){
    		for(int y=0;x;y=x,x=fa[x])
    			splay(x),sv[x]+=s[ch[x][1]]-s[y],ch[x][1]=y,pushup(x);
    	}
    	inline void makeroot(int x){
    		access(x);splay(x);pushr(x);
    	}
    	inline void split(int x,int y){
    		makeroot(x);access(y);splay(y);
    	}
    	inline void link(int x,int y){
    		makeroot(x);makeroot(y);//如果y不在根节点,那它祖先的虚子树信息也要一起修改;
    		fa[x]=y;
    		sv[y]+=s[x];
    	}
    	inline void cut(int x,int y){
    		split(x,y);
    		fa[x]=ch[y][0]=0;
    	}
    }using namespace LinkCutTree;
    
    int main(){
    	n=read(),q=read();
    	REP(t,1,q){
    		char ch;scanf("%c",&ch);
    		int x=read(),y=read();
    		if(ch=='Q'){
    			cut(x,y);makeroot(x);makeroot(y);
    			printf("%lld
    ",1ll*s[x]*s[y]);
    		}
    		link(x,y);
    	}
    	return 0;
    }
    
  • 相关阅读:
    Linux下批处理文件
    linux代码端启动终端
    ubuntu截图
    Ubuntu安装多个版本的Opencv
    Ubuntu双系统启动卡死
    Ubuntu14.04运行lsdslam与问题解决
    js懒加载
    公众号开发-获取用户信息
    ClipboardJS 复制文本功能
    css3 Gradient线性渐变详解
  • 原文地址:https://www.cnblogs.com/fruitea/p/12128425.html
Copyright © 2011-2022 走看看