zoukankan      html  css  js  c++  java
  • 【SPOJ QTREE6】Query on a tree VI(LCT维护树上同色连通块)

    点此看题面

    • 给定一棵(n)个点的树,初始所有点为黑色。
    • 要求支持两种操作:询问一个点所在同色连通块大小;翻转一个点的颜色。
    • (n,qle10^5)

    树上同色连通块

    容易想到去建两棵树,分别代表黑点和白点。

    一开始可能会很无脑地去想改变颜色就直接在原树上断边,新树上连边,但由于一个点的子节点可能有一大堆,复杂度爆炸。

    这里就有一个很巧妙的思路,既然我们有两棵树,干脆对于每个点分别维护好它是黑色/白色时,以它为最浅点的连通块信息(其他点就看作真实颜色)。

    这样一来,一大好处就是我们修改一个点的时候不需要再管它和儿子间的关系,只需要在对应的树中连上/断开它与父节点之间的边。

    而要询问(x)所在同色连通块的信息,首先(Access(x)),由于根节点必然是一个异色点(同色点会向父节点连边),因此我们(Splay)根节点,则此时根节点右儿子的子树信息就是我们要求的连通块信息了。

    这里注意一个细节问题,原树的根节点是没有父节点的,我们要给它建一个,否则询问时就始终会被当作异色点舍掉。

    此题要求同色连通块大小,也就是要维护子树大小,是(LCT)的一个经典操作,注意维护虚儿子子树大小之和即可。

    代码:(O(nlogn))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 100000
    #define add(x,y) (e[++ee].nxt=lnk[x],e[lnk[x]=ee].to=y)
    using namespace std;
    int n,a[N+5],ee,lnk[N+5];struct edge {int to,nxt;}e[2*N+5];
    namespace FastIO
    {
    	#define FS 100000
    	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
    	#define pc(c) (FC==FE&&(clear(),0),*FC++=c)
    	int OT;char oc,FI[FS],FO[FS],OS[FS],*FA=FI,*FB=FI,*FC=FO,*FE=FO+FS;
    	I void clear() {fwrite(FO,1,FC-FO,stdout),FC=FO;}
    	Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
    	Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
    	Tp I void writeln(Ty x) {W(OS[++OT]=x%10+48,x/=10);W(OT) pc(OS[OT--]);pc('
    ');}
    }using namespace FastIO;
    class LinkCutTree
    {
    	private:
    		#define PU(x) (O[x].Sz=O[O[x].S[0]].Sz+O[O[x].S[1]].Sz+O[x].G)//上传信息
    		#define IR(x) (O[O[x].F].S[0]^x&&O[O[x].F].S[1]^x)
    		#define Wh(x) (O[O[x].F].S[1]==x)
    		#define Co(x,y,d) (O[O[x].F=y].S[d]=x)
    		struct node {int Sz,G,F,S[2];}O[N+5];
    		I void Ro(RI x) {RI f=O[x].F,p=O[f].F,d=Wh(x);
    			!IR(f)&&(O[p].S[Wh(f)]=x),O[x].F=p,Co(O[x].S[d^1],f,d),Co(f,x,d^1),PU(f);}
    		I void S(RI x) {RI f;W(!IR(x)) f=O[x].F,!IR(f)&&(Ro(Wh(x)^Wh(f)?x:f),0),Ro(x);PU(x);}
    		I void Ac(RI x) {for(RI y=0;x;x=O[y=x].F) S(x),O[x].G+=O[O[x].S[1]].Sz-O[y].Sz,O[x].S[1]=y,PU(x);}
    	public:
    		I void Init() {for(RI i=1;i<=n;++i) O[i].Sz=O[i].G=1;}//每个点自己的大小一同记在虚儿子子树大小之和中
    		I void Link(CI x,CI y) {Ac(x),S(x),S(y),O[y].F=x,O[x].G+=O[y].Sz,PU(x);}//连边
    		I void Cut(CI x,CI y) {Ac(y),S(x),O[x].S[1]=O[y].F=0,PU(x);}//断边
    		I int Q(RI x) {Ac(x),S(x);W(O[x].S[0]) x=O[x].S[0];return S(x),O[O[x].S[1]].Sz;}//Access(x)并Splay根,询问根节点右儿子信息
    }LCT[2];
    int fa[N+5];I void dfs(CI x)//预处理建树
    {
    	for(RI i=lnk[x];i;i=e[i].nxt) e[i].to^fa[x]&&(fa[e[i].to]=x,dfs(e[i].to),0);LCT[0].Link(fa[x],x);
    }
    int main()
    {
    	RI i,x,y;for(read(n),i=1;i^n;++i) read(x,y),add(x,y),add(y,x);LCT[0].Init(),LCT[1].Init(),fa[1]=n+1,dfs(1);//给根节点建个父节点
    	RI Qt;read(Qt);W(Qt--) read(x,y),x?(LCT[a[y]].Cut(fa[y],y),LCT[a[y]^=1].Link(fa[y],y)):writeln(LCT[a[y]].Q(y));//翻转只需考虑与父节点的连边
    	return clear(),0;
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    程序员:不要自称为码农
    SpringBoot对静态资源配置
    LeetCode 572. Subtree of Another Tree(子树)
    LeetCode 437. Path Sum III(统计路径和等于sum的路径数量)
    LeetCode 112. Path Sum(判断路径和是否等于一个数)
    LeetCode 617. Merge Two Binary Trees(归并两棵二叉树)
    LeetCode 226. Invert Binary Tree(翻转二叉树)
    Failure to transfer org.apache.maven.plugins:maven-resources-plugin:pom:2.6 的解决办法
    linux-查询某软件的安装的目录
    WebService概念解释
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/SPOJ_QTREE6.html
Copyright © 2011-2022 走看看