zoukankan      html  css  js  c++  java
  • [SDOI2017]树点涂色

    XVII.[SDOI2017]树点涂色

    树剖和LCT学到最后实际上是殊途同归的……就比如说这题,可以用树剖,但是在操作\(1\)中借鉴了LCT的跳链思想;LCT则因为不能子树修改,按照dfs序需要建出线段树出来,实际上也就是树剖的思想了。

    首先讲一下LCT写法。观察到任意时刻,任意一种颜色一定是一条深度递增的链。那么刚好可以被存入LCT的一颗splay中。

    因此操作\(1\)就是直接access(x)即可。

    关于操作\(2\),我们可以采用树上差分的思想。观察到答案具有可减性。我们设\(res_x\)表示节点\(x\)到根的路径上颜色段数,则路径\((x,y)\)的答案即为\(res_x+res_y-res_{LCA_{x,y}}+1\),其中\(+1\)是因为\(LCA_{x,y}\)所在的那段颜色被减了两遍。

    操作\(3\)维护dfs序线段树直接求子树\(res\)最值即可(类似于树剖操作)。

    至于如何维护\(res\)的值呢?我们思考一下,发现它就是你把它access时,所经过的splay的数量。老办法,考虑虚子树的信息。我们只有在access时才会更改虚子树的关系。

    我们回忆一下access时我们干了点什么:

    inline void access(int x){
    	for(register int y=0;x;x=t[y=x].fa)splay(x),rson=y,pushup(x);
    }
    

    我们断掉rson的实边,并增加了y的实边。

    对于y,少了一个splay,应该整棵子树\(-1\);对于rson,多了一个splay,应该整棵子树\(+1\)

    但是!!!别忘了,splay中一切父子关系都是不可靠的。我们必须找到真正的rson和真正的y。而真正的rson和真正的y,就是以它们为根的splay中,深度最浅的点。

    然后我们写出了这样的找根函数:

    inline int find(int x){
    	pushdown(x);
    	while(lson)pushdown(x),x=lson;
    	return x;
    }
    

    使用这个就能找到正确的子节点。

    正确的access

    inline void access(int x){
    	for(register int y=0,z;x;x=t[y=x].fa){
    		splay(x);
    		if(rson)z=find(rson),modify(1,1,n,dfn[z],dfn[z]+sz[z]-1,1);
    		rson=y;
    		if(y)z=find(y),modify(1,1,n,dfn[z],dfn[z]+sz[z]-1,-1);
    		pushup(x);
    	}
    }
    

    总代码:

    #include<bits/stdc++.h>
    using namespace std;
    int n,m;
    //---------------------------------------------------------
    struct Edge{
    	int to,next;
    }edge[200100];
    struct SegTree{
    	int tag,mx;
    }seg[400100];
    struct LCT{
    	int fa,ch[2];
    	bool rev;
    }t[100100];
    //---------------------------------------------------------
    int head[100100],cnt,sz[100100];
    void ae(int u,int v){
    	edge[cnt].next=head[u],edge[cnt].to=v,head[u]=cnt++;
    	edge[cnt].next=head[v],edge[cnt].to=u,head[v]=cnt++;
    }
    //---------------------------------------------------------
    int dfn[100100],dep[100100],tot,rev[100100];
    int anc[100100][18];
    void dfs(int x,int fa){
    	dep[x]=dep[fa]+1,dfn[x]=++tot,t[x].fa=fa,sz[x]=1,rev[tot]=x,anc[x][0]=fa;
    	for(int i=1;(1<<i)<dep[x];i++)anc[x][i]=anc[anc[x][i-1]][i-1];
    	for(int i=head[x];i!=-1;i=edge[i].next)if(edge[i].to!=fa)dfs(edge[i].to,x),sz[x]+=sz[edge[i].to];
    }
    int LCA(int u,int v){
    	if(dep[u]>dep[v])swap(u,v);
    	for(int i=17;i>=0;i--)if(dep[u]<=dep[v]-(1<<i))v=anc[v][i];
    	if(u==v)return u;
    	for(int i=17;i>=0;i--)if(anc[u][i]!=anc[v][i])u=anc[u][i],v=anc[v][i];
    	return anc[u][0];
    }
    //---------------------------------------------------------
    #define lson x<<1
    #define rson x<<1|1
    #define mid ((l+r)>>1)
    void update(int x){seg[x].mx=max(seg[lson].mx,seg[rson].mx);}
    void ADD(int x,int y){seg[x].tag+=y,seg[x].mx+=y;}
    void downdate(int x){ADD(lson,seg[x].tag),ADD(rson,seg[x].tag),seg[x].tag=0;}
    void build(int x,int l,int r){
    	if(l==r){seg[x].mx=dep[rev[l]];return;}
    	build(lson,l,mid),build(rson,mid+1,r),update(x);
    }
    void modify(int x,int l,int r,int L,int R,int val){
    	if(l>R||r<L)return;
    	if(L<=l&&r<=R){ADD(x,val);return;}
    	downdate(x),modify(lson,l,mid,L,R,val),modify(rson,mid+1,r,L,R,val),update(x);
    }
    int query(int x,int l,int r,int L,int R){
    	if(l>R||r<L)return 0;
    	if(L<=l&&r<=R)return seg[x].mx;
    	downdate(x);
    	return max(query(lson,l,mid,L,R),query(rson,mid+1,r,L,R));
    }
    #undef lson
    #undef rson
    //---------------------------------------------------------
    #define lson t[x].ch[0]
    #define rson t[x].ch[1]
    inline int identify(int x){
    	if(x==t[t[x].fa].ch[0])return 0;
    	if(x==t[t[x].fa].ch[1])return 1;
    	return -1;
    }
    inline void pushup(int x){}
    inline void REV(int x){t[x].rev^=1,swap(lson,rson);}
    inline void pushdown(int x){
    	if(!t[x].rev)return;
    	if(lson)REV(lson);
    	if(rson)REV(rson);
    	t[x].rev=0;
    }
    inline void rotate(int x){
    	register int y=t[x].fa;
    	register int z=t[y].fa;
    	register int dirx=identify(x);
    	register int diry=identify(y);
    	register 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[y].fa=x,t[x].ch[!dirx]=y;
    	pushup(y),pushup(x);
    }
    inline void pushall(int x){if(identify(x)!=-1)pushall(t[x].fa);pushdown(x);}
    inline void splay(int x){
    	pushall(x);
    	while(identify(x)!=-1){
    		register int fa=t[x].fa;
    		if(identify(fa)==-1)rotate(x);
    		else if(identify(x)==identify(fa))rotate(fa),rotate(x);
    		else rotate(x),rotate(x);
    	}
    }
    inline int find(int x){
    	pushdown(x);
    	while(lson)pushdown(x),x=lson;
    	return x;
    }
    inline void access(int x){
    	for(register int y=0,z;x;x=t[y=x].fa){
    		splay(x);
    		if(rson)z=find(rson),modify(1,1,n,dfn[z],dfn[z]+sz[z]-1,1);
    		rson=y;
    		if(y)z=find(y),modify(1,1,n,dfn[z],dfn[z]+sz[z]-1,-1);
    		pushup(x);
    	}
    }
    #undef lson
    #undef rson
    //---------------------------------------------------------
    int main(){
    	scanf("%d%d",&n,&m),memset(head,-1,sizeof(head));
    	for(int i=1,x,y;i<n;i++)scanf("%d%d",&x,&y),ae(x,y);
    	dfs(1,0),build(1,1,n);
    	for(int i=1,t1,t2,t3;i<=m;i++){
    		scanf("%d%d",&t1,&t2);
    		if(t1==1)access(t2);
    		if(t1==2){
    			scanf("%d",&t3);
    			int lca=LCA(t2,t3);
    			lca=query(1,1,n,dfn[lca],dfn[lca]);
    			t2=query(1,1,n,dfn[t2],dfn[t2]);
    			t3=query(1,1,n,dfn[t3],dfn[t3]);
    			printf("%d\n",t2+t3-2*lca+1);
    		}
    		if(t1==3)printf("%d\n",query(1,1,n,dfn[t2],dfn[t2]+sz[t2]-1));
    	}
    	return 0;
    }
    

  • 相关阅读:
    前台的js对象数组传到后台处理。在前台把js对象数组转化为json字符串,在后台把json字符串解析为List<>
    ef中用lambda expressions时要注意(m=>m.id ==b ) 此时的b只能是基本的数据类型 。连属性都不能用
    你是否有遇到过某个实体类字段(属性)过多的情况,不想每次点的话戳进来(C# 反射)
    razor使用注意点........
    让简历在15秒内吸引招聘者《我的前程我做主》六
    JavaScript学习总结(十六)——Javascript闭包(Closure)
    C#操作XML的完整例子——XmlDocument篇(转载,仅做学习之用)
    C#操作XML方法集合
    图片超链接作为下载来处理
    架构师手记
  • 原文地址:https://www.cnblogs.com/Troverld/p/14602098.html
Copyright © 2011-2022 走看看