• luogu题解P2486[SDOI2011]染色--树链剖分+trick


    题目链接

    https://www.luogu.org/problemnew/show/P2486

    分析

    看上去又是一道强行把序列上问题搬运到树上的裸题,然而分析之后发现并不然...

    首先我们考虑如何在序列上维护信息:从最简单的想起,如果两个相邻的元素合并,显然是这两个元素所含颜色段个数(其实就是1)加起来,如果两个元素颜色相同就减1;那么两个分别含有两个元素的相邻区间合并,还是把这两个区间所含颜色段个数加起来,如果左区间最右边的颜色等于右区间最左边的颜色就减去1.

    如此我们已经得到线段树维护信息的方法,记录区间所含颜色段个数,区间最左边颜色及区间最右边颜色,当然为了(pushdown)我们还得维护一个(tag)数组表示覆盖标记,然后按上面方法就好了

    但是在树链剖分查询两点之间时就与序列上不同了.有一个问题,就是当前链最左边的颜色如果和上面那条链最右边的颜色相等的话,需要将贡献减1.有一个(naiive)的方法是每次查询链时再查一下上面那条链最右边的颜色(其实就是单点查询(fa[top[x]])的颜色),然后这个方法看起来不优美,其实有个更妙的方法

    我们每次查完一条链后记录该链最左边的颜色,同时将该链最右边的颜色与上一次记录的值比较。看起来很容易但有个问题就是你可能是从(LCA)两个不同的子树上向LCA跳,然后从 @ qscqesze_lca 的题解中学到了一个小trick轻易解决了这个问题,请看代码

    代码

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cctype>
    #include <cstdlib>
    #include <vector>
    #define ll long long 
    #define ri register int 
    #define ull unsigned long long 
    using std::min;
    using std::max;
    using std::swap;
    template <class T>inline void read(T &x){
    	x=0;int ne=0;char c;
    	while(!isdigit(c=getchar()))ne=c=='-';
    	x=c-48;
    	while(isdigit(c=getchar()))x=(x<<3)+(x<<1)+c-48;
    	x=ne?-x:x;return ;
    }
    const int maxn=100005;
    const int inf=0x7fffffff;
    int n,m;
    struct Edge{
    	int ne,to;
    }edge[maxn<<1];
    int h[maxn],num_edge=1;
    inline void add_edge(int f,int to){
    	edge[++num_edge].ne=h[f];
    	edge[num_edge].to=to;
    	h[f]=num_edge;
    }
    int col[maxn];
    int dep[maxn],fa[maxn],size[maxn],dfn[maxn],rnk[maxn],tot=0,top[maxn],son[maxn];
    void dfs_1(int now){
    	int v;size[now]=1;
    	for(ri i=h[now];i;i=edge[i].ne){
    		v=edge[i].to;
    		if(v==fa[now])continue;
    		dep[v]=dep[now]+1,fa[v]=now;
    		dfs_1(v);
    		size[now]+=size[v];
    		if(!son[now]||size[v]>size[son[now]])son[now]=v;
    	}
    	return ;
    }
    void dfs_2(int now,int t){
    	int v;top[now]=t,dfn[now]=++tot,rnk[tot]=now;
    	if(!son[now])return ;
    	dfs_2(son[now],t);
    	for(ri i=h[now];i;i=edge[i].ne){
    		v=edge[i].to;
    		if(v==fa[now]||v==son[now])continue;
    		dfs_2(v,v);
    	}
    	return ;
    }
    int num[maxn<<2],lc[maxn<<2],rc[maxn<<2],tag[maxn<<2];
    int L,R,dta;
    void build(int now,int l,int r){
    	tag[now]=-1;
    	if(l==r){
    		num[now]=1;
    		lc[now]=rc[now]=col[rnk[l]];
    		return ;
    	}
    	int mid=(l+r)>>1;
    	build(now<<1,l,mid);
    	build(now<<1|1,mid+1,r);
    	num[now]=num[now<<1]+num[now<<1|1]-(rc[now<<1]==lc[now<<1|1]?1:0);
    	lc[now]=lc[now<<1],rc[now]=rc[now<<1|1];
    	return ;
    }
    inline void pushdown(int now){
    	if(tag[now]!=-1){
    		tag[now<<1]=tag[now<<1|1]=tag[now];
    		lc[now<<1]=lc[now<<1|1]=lc[now];
    		rc[now<<1]=rc[now<<1|1]=rc[now];
    		num[now<<1]=num[now<<1|1]=1;
    		tag[now]=-1;
    	}
    	return ;
    }
    void update(int now,int l,int r){
    	if(L<=l&&r<=R){
    		num[now]=1;
    		lc[now]=rc[now]=dta;
    		tag[now]=dta;
    		return ;
    	}
    	int mid=(l+r)>>1;
    	pushdown(now);
    	if(L<=mid)update(now<<1,l,mid);
    	if(mid<R)update(now<<1|1,mid+1,r);
    	num[now]=num[now<<1]+num[now<<1|1]-(rc[now<<1]==lc[now<<1|1]?1:0);
    	lc[now]=lc[now<<1],rc[now]=rc[now<<1|1];
    	return ;
    }
    int chain_lc,chain_rc;
    int query(int now,int l,int r){
    	if(L==l)chain_lc=lc[now];
    	if(R==r)chain_rc=rc[now];
    	if(L<=l&&r<=R){
    		return num[now];
    	}
    	int ans=0,mid=(l+r)>>1;
    	pushdown(now);
    	if(L<=mid&&mid<R)ans=query(now<<1,l,mid)+query(now<<1|1,mid+1,r)-(rc[now<<1]==lc[now<<1|1]?1:0);
    	else if(L<=mid)ans=query(now<<1,l,mid);
    	else if(mid<R)ans=query(now<<1|1,mid+1,r);
        //num[now]=num[now<<1]+num[now<<1|1]-(rc[now<<1]==lc[now<<1|1]?1:0);
    	//lc[now]=lc[now<<1],rc[now]=rc[now<<1|1];
    	return ans;
    }
    inline void update_path(int x,int y,int c){
    	dta=c;
    	while(top[x]!=top[y]){
    		if(dep[top[x]]<dep[top[y]])swap(x,y);
    		L=dfn[top[x]],R=dfn[x];
    		update(1,1,n);
    		x=fa[top[x]];
    	}
    	if(dfn[x]>dfn[y])swap(x,y);
    	L=dfn[x],R=dfn[y];
    	update(1,1,n);
    	return ;
    }
    int lst_1,lst_2;//lst_1总是你当前正在查询的链的上一条链的最左边颜色 
    inline void query_path(int x,int y){
    	int ans=0;
    	lst_1=lst_2=0;
    	while(top[x]!=top[y]){
    		if(dep[top[x]]<dep[top[y]])
    		   {swap(x,y),swap(lst_1,lst_2);}//非常高明的一个trick,不用刻意查询链top父亲的颜色 
    		L=dfn[top[x]],R=dfn[x];	
    		ans+=query(1,1,n);
    		if(lst_1==chain_rc)ans--;
    		lst_1=chain_lc;
    		x=fa[top[x]];
    	}
    	if(dfn[x]<dfn[y])
    		{swap(x,y),swap(lst_1,lst_2);}
    	L=dfn[y],R=dfn[x];
    	ans+=query(1,1,n);
    	if(chain_rc==lst_1)ans--;
    	if(chain_lc==lst_2)ans--;
    	printf("%d
    ",ans);
    	return ;
    }
    int main(){
    	int x,y,z;
    	read(n),read(m);
    	for(ri i=1;i<=n;i++)read(col[i]);
    	for(ri i=1;i<n;i++){
    		read(x),read(y);
    		add_edge(x,y);
    		add_edge(y,x);
    	}
    	dep[1]=1,fa[1]=0;
    	dfs_1(1);
    	dfs_2(1,1);
    	build(1,1,n);
    	char opt[5];
    	while(m--){
    		scanf("%s",opt);
    		if(opt[0]=='C'){
    			read(x),read(y),read(z);
    			update_path(x,y,z);
    		}
    		else{
    			read(x),read(y);
    			query_path(x,y);
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    企业要主动淘汰五种人
    人力管理的核心:选、用、育、留
    张瑞敏:正确路线确定后,干部就是决定因素
    西点军校如何培养学员
    董事长如何找合适的搭档
    企业家何时应该放权
    会计基础视频
    同样劳动,为何结果不同?
    什么是真正的工作到位
    中国的人口和经济周期
  • 原文地址:https://www.cnblogs.com/Rye-Catcher/p/9478885.html
Copyright © 2020-2023  润新知