• [SHOI2014]三叉神经树


    VII.[SHOI2014]三叉神经树

    LCT相较于树剖,最大的优势就是可以把一条链上的东西全整到一个splay里面,不像树剖在多个重链进行合并时会有很大麻烦。更好的是,LCT复杂度是\(O(n\log n)\)的,树剖不仅码量大,思路复杂,复杂度还是恶心的\(O(n\log^2n)\)

    这题就是典型的树剖被LCT全方面完爆。

    首先,这道题要考阅读理解。翻译一下,就是给你一棵树,树上的每个节点要么有三个儿子,要么是叶子。每个叶子初始时有一个或\(0\)\(1\)的权值。每个非叶子节点的权值由它三个儿子的权值决定,\(0\)\(1\)在它儿子中占比更多的那一个即是它的权值。现在我们每次修改一个叶子的权值,要你输出修改后根的输出。

    我们可以发现,在修改一个叶子时,只有叶子到根的这条路径上点会受到影响。并且,影响必定是连续的一段,一旦有一个点没有被修改,那这次修改就到头了。

    设一个节点三个儿子的权值和为\(sum\),则如果\(sum>1\),这个节点输出为\(1\);否则,为\(0\)

    则每次修改,假设是\(0\)\(1\),只有从叶子到根的一段连续的\(sum\)\(1\)的点才会被修改。\(sum\)\(0\)的,多一个\(1\)的儿子只会让\(sum\)变成\(1\),输出还是\(0\)\(sum\)\(2\)\(3\)的,更不用说了,\(sum\)增加它的权值还是\(1\)。只有\(sum\)\(1\)的会受到影响。

    则我们只需要维护路径上深度最大的非\(1\)节点。这样,比它深的所有节点都有修改。

    对于我们修改一个叶子节点\(x\)时:

    令它的父亲为\(y\)。则我们\(access(y)\),打通从\(y\)\(ROOT\)的路径。

    等等,为什么是\(y\)?不应该是\(x\)吗?

    因为\(x\)的输入不由\(sum_x\)决定,你要是这么\(access(x)\)会让\(x\)变成普通节点,由它的\(sum_x\)决定,而\(sum_x\)始终为\(0\)

    并且,如果这样的话,我们就不能直接找到\(x\)的父亲,因为同一splay中的父亲不一定是真父亲。

    我们\(access(y)\)后,\(splay(y)\),并找到维护的非\(1\)节点的位置,设为\(id_1\)

    如果\(id_1\)不存在,说明从叶子到根这一整条路径上所有的点都得改,直接在\(y\)上打\(tag\)

    否则,设\(z=id_1\)。则\(splay(z)\)后,\(z\)的右儿子即是我们修改的目标。我们直接在\(z\)的右儿子上打\(tag\),同时更新\(z\)\(sum\)

    现在我们来讨论一下怎么维护\(id\)

    先上函数:

    void pushup(int x){
    	if(t[rson].id[1])t[x].id[1]=t[rson].id[1];
    	else if(t[x].sum!=1)t[x].id[1]=x;
    	else t[x].id[1]=t[lson].id[1];
    	if(t[rson].id[2])t[x].id[2]=t[rson].id[2];
    	else if(t[x].sum!=2)t[x].id[2]=x;
    	else t[x].id[2]=t[lson].id[2];
    }
    

    因为深度越大越靠右,我们转移时优先选择右儿子,其次自己,最次左儿子。

    至于这个\(id_2\),则是在叶子节点的输入由\(1\)\(0\)时用的,因为由\(1\)\(0\)只会影响到\(sum=2\)的节点。

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    #define lson t[x].ch[0]
    #define rson t[x].ch[1]
    int n,m,in[1501000],ans;
    struct LCT{
    	int ch[2],id[3],fa,sum,tag,val;
    }t[1501000];
    int identify(int x){
    	if(t[t[x].fa].ch[0]==x)return 0;
    	if(t[t[x].fa].ch[1]==x)return 1;
    	return -1;
    }
    void pushup(int x){
    	if(t[rson].id[1])t[x].id[1]=t[rson].id[1];
    	else if(t[x].sum!=1)t[x].id[1]=x;
    	else t[x].id[1]=t[lson].id[1];
    	if(t[rson].id[2])t[x].id[2]=t[rson].id[2];
    	else if(t[x].sum!=2)t[x].id[2]=x;
    	else t[x].id[2]=t[lson].id[2];
    }
    void modi(int x,int tag){
    	t[x].sum+=tag,t[x].val=(t[x].sum>1);
    	swap(t[x].id[1],t[x].id[2]);
    	t[x].tag+=tag;
    }
    void pushdown(int x){
    	if(!t[x].tag)return;
    	if(lson)modi(lson,t[x].tag);
    	if(rson)modi(rson,t[x].tag);
    	t[x].tag=0;
    }
    void rotate(int x){
    	int y=t[x].fa;
    	int z=t[y].fa;
    	int dirx=identify(x);
    	int diry=identify(y);
    	int b=t[x].ch[!dirx];
    	if(diry!=-1)t[z].ch[diry]=x;t[x].fa=z;
    	if(b)t[b].fa=y;t[y].ch[dirx]=b;
    	t[x].ch[!dirx]=y,t[y].fa=x;
    	pushup(y),pushup(x);
    }
    void pushall(int x){
    	if(identify(x)!=-1)pushall(t[x].fa);
    	pushdown(x);
    }
    void splay(int x){
    	pushall(x);
    	while(identify(x)!=-1){
    		int fa=t[x].fa;
    		if(identify(fa)==-1)rotate(x);
    		else if(identify(fa)==identify(x))rotate(fa),rotate(x);
    		else rotate(x),rotate(x);
    	}
    	pushup(x);
    }
    void access(int x){
    	for(int y=0;x;x=t[y=x].fa)splay(x),rson=y,pushup(x);
    }
    queue<int>q;
    int main(){
    	scanf("%d",&n);
    	for(int i=1,t1,t2,t3;i<=n;i++)scanf("%d%d%d",&t1,&t2,&t3),t[t1].fa=t[t2].fa=t[t3].fa=i,in[i]=3;
    	for(int i=n+1;i<=3*n+1;i++)scanf("%d",&t[i].val),q.push(i);
    	while(!q.empty()){
    		int x=q.front();q.pop();
    		if(!t[x].fa)continue;
    		if(x<=n)pushup(x); 
    		t[t[x].fa].sum+=t[x].val,in[t[x].fa]--;
    		if(!in[t[x].fa])t[t[x].fa].val=(t[t[x].fa].sum>1),q.push(t[x].fa);
    	}
    	ans=t[1].val;
    	scanf("%d",&m);
    	for(int i=1,x,y,z;i<=m;i++){
    		scanf("%d",&x),y=t[x].fa;
    		access(y),splay(y);
    		int dir=t[x].val?2:1,val=t[x].val?-1:1;
    		if(t[y].id[dir]){
    			z=t[y].id[dir];
    			splay(z);
    			modi(t[z].ch[1],val),pushup(t[z].ch[1]);
    			t[z].sum+=val,t[z].val=(t[z].sum>1),pushup(z);
    		}else modi(y,val),ans^=1,pushup(y);
    		t[x].val^=1;
    		printf("%d\n",ans);
    	}
    	return 0;
    }
    

  • 相关阅读:
    SpringBoot使用token简单鉴权
    SpringBoot-dubbo自定义负载均衡实现简单灰度
    SpringBoot基于数据库实现简单的分布式锁
    使用canal增量同步mysql数据库信息到ElasticSearch
    使用go-mysql-elasticsearch同步mysql数据库信息到ElasticSearch
    使用logstash同步mysql数据库信息到ElasticSearch
    extract()函数:用于从一个date或者interval类型中截取到特定的部分
    转://IO的基础概念
    转://MySQL客户端工具的选择
    转://oracle 软件的收费模式
  • 原文地址:https://www.cnblogs.com/Troverld/p/14601930.html
Copyright © 2020-2023  润新知