• [SDOI2017][bzoj4817] 树点涂色 [LCT+线段树]


    题面

    传送门

    思路

    $LCT$

    我们发现,这个1操作,好像非常像$LCT$里面的$Access$啊~

    那么我们尝试把$Access$操作魔改成本题中的涂色

    我们令$LCT$中的每一个$splay$链代表同一种颜色的一条链,那么$Access(u)$就相当于把这一段变成同一种颜色

    注意这个东西能成立,是因为每次涂上的都是新的一种颜色(所以如果有$m$种颜色,每次涂其中一种,可能重复的之类的就不能这么做了)

    线段树

    接下来我们解决询问的问题:什么结构能维护链上信息和子树信息(同时)?当然是线段树了~

    我们考虑开一棵以$dfs$序为下标的线段树,线段树的每个叶节点保存这个节点到根的路径权值,其他节点维护对应区间的最大值

    这样,询问三就变成了区间求$max$,询问二则可以化成$w[u]+w[v]-2*w[lca]+1$这样的形式($w[u]$表示$u$到根路径上的权值)(这个东西不管颜色怎么分布,一定是对的,证明很容易,可以自己想想)

    但是这样之后,我们在修改一的时候,怎么修改线段树上的值呢?

    维护线段树

    我们发现,在修改的过程中,每一次我们从当前$splay$连向上面的$splay$时,会把一条虚边变成重边、一条重边变成虚边

    那么,原来重边下的这棵子树,因为它上面的东西变成了新的颜色,而上面的东西本来是和它一个颜色的,所以重边的这棵子树中所有节点的权值要+1

    而对于原来虚边下面的这棵子树来说,它上面的东西本来和它不是一个颜色的,现在是同一个颜色了,所以虚边的子树中所有节点的权值要-1

    这样,我们只要在$Access$的时候,同时维护线段树的权值就可以了

    总结

    本题从与$Access$相似的修改方式开始,切入点是$LCT$,然后加入了线段树维护,并且确定了在$Access$操作的同时修改线段树的值

    总时间复杂度为均摊$O(nlog^2n)$

    Code

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cassert>
    #include<cmath>
    #define ll long long
    using namespace std;
    inline int read(){
    	int re=0,flag=1;char ch=getchar();
    	while(ch>'9'||ch<'0'){
    		if(ch=='-') flag=-1;
    		ch=getchar();
    	}
    	while(ch>='0'&&ch<='9') re=(re<<1)+(re<<3)+ch-'0',ch=getchar();
    	return re*flag;
    }
    int n,Q,first[100010],cnte,dep[100010],st[100010][20],dfn[100010],lim[100010],back[100010],cntn;
    int fa[100010];
    struct edge{
    	int to,next;
    }e[200010];
    inline void add(int u,int v){
    	e[++cnte]=(edge){v,first[u]};first[u]=cnte;
    	e[++cnte]=(edge){u,first[v]};first[v]=cnte;
    }
    void dfs(int u,int f){
    	int i,v;dep[u]=dep[f]+1;st[u][0]=f;fa[u]=f;//这里把LCT的fa数组预处理好(此时没有边,都是虚边)
    	dfn[u]=++cntn;back[cntn]=u;
    	for(i=first[u];~i;i=e[i].next){
    		v=e[i].to;if(v==f) continue;
    		dfs(v,u);
    	}
    	lim[u]=cntn;
    }
    void ST(){
    	for(int j=1;j<=19;j++){
    		for(int i=1;i<=n;i++) st[i][j]=st[st[i][j-1]][j-1];
    	}
    }
    int lca(int l,int r){
    	int i;
    	if(dep[l]>dep[r]) swap(l,r);
    	for(i=19;i>=0;i--) if(dep[st[r][i]]>=dep[l]) r=st[r][i];
    	if(l==r) return l;
    	for(i=19;i>=0;i--){
    		if(st[r][i]!=st[l][i]){
    			l=st[l][i];
    			r=st[r][i];
    		}
    	}
    	return st[l][0];
    }
    //segment tree
    int a[400010],lazy[400010];
    void update(int num){
    	a[num]=max(a[num<<1],a[num<<1|1]);
    }
    void push(int l,int r,int num){
    	if(l==r||!lazy[num]) return;
    	a[num<<1]+=lazy[num];a[num<<1|1]+=lazy[num];
    	lazy[num<<1]+=lazy[num];lazy[num<<1|1]+=lazy[num];
    	lazy[num]=0;
    }
    void build(int l,int r,int num){
    	if(l==r){a[num]=dep[back[l]];return;}
    	int mid=(l+r)>>1;
    	build(l,mid,num<<1);build(mid+1,r,num<<1|1);
    	update(num);
    }
    void change(int l,int r,int ql,int qr,int num,int ch){
    	push(l,r,num);
    	if(l>=ql&&r<=qr){a[num]+=ch;lazy[num]+=ch;return;}
    	int mid=(l+r)>>1;
    	if(mid>=ql) change(l,mid,ql,qr,num<<1,ch);
    	if(mid<qr) change(mid+1,r,ql,qr,num<<1|1,ch);
    	update(num);
    }
    int query(int l,int r,int ql,int qr,int num){
    	push(l,r,num);
    	if(l>=ql&&r<=qr) return a[num];
    	int mid=(l+r)>>1,re=-1e9;
    	if(mid>=ql) re=max(re,query(l,mid,ql,qr,num<<1));
    	if(mid<qr) re=max(re,query(mid+1,r,ql,qr,num<<1|1));
    	return re;
    }
    //link cut tree
    int ch[100010][2]={0},rt[100010];
    int get(int pos){return ch[fa[pos]][1]==pos;}
    void rotate(int x){
    	int f=fa[x],ff=fa[f],son=get(x);
    	ch[f][son]=ch[x][son^1];
    	if(ch[f][son]) fa[ch[f][son]]=f;
    	fa[f]=x;ch[x][son^1]=f;
    	fa[x]=ff;
    	if(rt[f]) rt[x]=1,rt[f]=0;
    	else ch[ff][ch[ff][1]==f]=x;
    }
    void splay(int pos){
    	if(rt[pos]) return;
    	for(int f;!rt[pos];rotate(pos)){
    		if(!rt[f=fa[pos]])
    			rotate((get(f)==get(pos))?f:pos);
    	}
    }
    int pre(int pos){
    	while(ch[pos][0]) pos=ch[pos][0];
    	return pos;
    }
    void access(int pos){
    	for(int tmp=0,tt;pos;tmp=pos,pos=fa[pos]){
    		splay(pos);
    		if(ch[pos][1]){//重边变虚边,+1
    			tt=pre(ch[pos][1]);
    			change(1,n,dfn[tt],lim[tt],1,1);
    		}
    		rt[ch[pos][1]]=1;ch[pos][1]=tmp;rt[tmp]=0;
    		if(tmp){//虚边变重边,-1
    			tt=pre(tmp);
    			change(1,n,dfn[tt],lim[tt],1,-1);
    		}
    	}
    }
    int main(){
    	memset(first,-1,sizeof(first));
    	n=read();Q=read();int i,t1,t2,t3,f;
    	for(i=1;i<n;i++){
    		t1=read();t2=read();
    		add(t1,t2);
    	}
    	dfs(1,0);ST();build(1,n,1);
    	for(i=1;i<=n;i++) rt[i]=1;
    	while(Q--){
    		t1=read();
    		if(t1==1){
    			t2=read();access(t2);
    		}
    		if(t1==2){
    			t2=read();t3=read();
    			f=lca(t2,t3);
    			printf("%d
    ",query(1,n,dfn[t2],dfn[t2],1)+query(1,n,dfn[t3],dfn[t3],1)-2*query(1,n,dfn[f],dfn[f],1)+1);
    		}
    		if(t1==3){
    			t2=read();
    			printf("%d
    ",query(1,n,dfn[t2],lim[t2],1)); 
    		}
    	}
    }
    
  • 相关阅读:
    hdu 4115 石头剪子布(2-sat问题)
    AFNetWorking POST Multi-Part Request 上传图片
    左右c++与java中国的垃圾问题的分析与解决
    ACM核武器
    MAX2323E
    cocos2d-x 发动机分析:程序如何开始和结束?
    STL 源代码分析 算法 stl_heap.h
    Android 4.4(KitKat)表格管理子系统
    Swift
    Swift
  • 原文地址:https://www.cnblogs.com/dedicatus545/p/9406690.html
Copyright © 2020-2023  润新知