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;
    }
    
  • 相关阅读:
    LeetCode Merge Two Sorted Lists 归并排序
    LeetCode Add Binary 两个二进制数相加
    LeetCode Climbing Stairs 爬楼梯
    034 Search for a Range 搜索范围
    033 Search in Rotated Sorted Array 搜索旋转排序数组
    032 Longest Valid Parentheses 最长有效括号
    031 Next Permutation 下一个排列
    030 Substring with Concatenation of All Words 与所有单词相关联的字串
    029 Divide Two Integers 两数相除
    028 Implement strStr() 实现 strStr()
  • 原文地址:https://www.cnblogs.com/fruitea/p/12128425.html
Copyright © 2011-2022 走看看