返回顶部

染色

[SDOI2011] 染色

题目描述

给定一棵 \(n\) 个节点的无根树,共有 \(m\) 个操作,操作分为两种:

  1. 将节点 \(a\) 到节点 \(b\) 的路径上的所有点(包括 \(a\)\(b\))都染成颜色 \(c\)
  2. 询问节点 \(a\) 到节点 \(b\) 的路径上的颜色段数量。

颜色段的定义是极长的连续相同颜色被认为是一段。例如 112221 由三段组成:112221

输入格式

输入的第一行是用空格隔开的两个整数,分别代表树的节点个数 \(n\) 和操作个数 \(m\)

第二行有 \(n\) 个用空格隔开的整数,第 \(i\) 个整数 \(w_i\) 代表结点 \(i\) 的初始颜色。

\(3\) 到第 \((n + 1)\) 行,每行两个用空格隔开的整数 \(u, v\),代表树上存在一条连结节点 \(u\) 和节点 \(v\) 的边。

\((n + 2)\) 到第 \((n + m + 1)\) 行,每行描述一个操作,其格式为:

每行首先有一个字符 \(op\),代表本次操作的类型。

  • \(op\)C,则代表本次操作是一次染色操作,在一个空格后有三个用空格隔开的整数 \(a, b, c\),代表将 \(a\)\(b\) 的路径上所有点都染成颜色 \(c\)
  • \(op\)Q,则代表本次操作是一次查询操作,在一个空格后有两个用空格隔开的整数 \(a, b\),表示查询 \(a\)\(b\) 路径上的颜色段数量。

输出格式

对于每次查询操作,输出一行一个整数代表答案。

样例 #1

样例输入 #1

6 5
2 2 1 2 1 1
1 2
1 3
2 4
2 5
2 6
Q 3 5
C 2 1 1
Q 3 5
C 5 1 2
Q 3 5

样例输出 #1

3
1
2

提示

数据规模与约定

对于 \(100\%\) 的数据,\(1 \leq n, m \leq 10^5\)\(1 \leq w_i, c \leq 10^9\)\(1 \leq a, b, u, v \leq n\)\(op\) 一定为 CQ,保证给出的图是一棵树。

除原数据外,还存在一组不计分的 hack 数据。

这题让我想到了山海经,这道题用树链剖分,记录每个区间左端点和右端点的颜色,如果左孩子的右区间=右孩子的左区间,那么答案需要减1,注意容易遗漏一点,就是树链剖分的时候我们是一条一条的链
当然不能自然地把路径上的颜色带累加
我们发现树剖求LCA是利用两个点按照深度向上跳最后跳到一起的方法
自下而上,根据深度交替向上跳
而这个路径可以看成‘人’字型

我们不妨把这个路径分成两边左边和右边我们再用变量
pos1表示当前要往上跳的路径\上次的终点颜色(值得注意的是,终点是左端点的颜色,因为dfs序从小到大连续的,根节点是较小的点)
pos2表示另一条路径\上一次的终点颜色
这样,如果当前往上跳的路径\这次的起点颜色等于 当前要往上跳的路径\上次的终点颜色 那么颜色段数量-1
还有最后的时候,top[u]=top[v]的时候,即已经在同一个重链上时,两边端点颜色都要考虑与对应ans比较颜色,相同答案要相应减一

点击查看代码
#include <bits/stdc++.h>
#define ll long long
#define register int int
#define lid (rt<<1)
#define rid (rt<<1|1)
using namespace std;
const int N = 1e6+5; 
int n,m;
struct Tree
{
	int l,r,lz;ll lc,rc,cnt;
}st[N<<4];
struct ac
{
	int u,to,next;
}edge[N*2];
int cnt=0,head[N];
void add(int u,int v)
{
	edge[++cnt].u=u;
	edge[cnt].to=v;
//	edge[cnt].w=w;
	edge[cnt].next=head[u];
	head[u]=cnt;
}
int dep[N],fa[N],top[N],ntime,dfn[N],size[N],son[N],rnk[N];ll w[N];
void _pushup(int rt)
{
	if(st[lid].rc==st[rid].lc)
	{
		st[rt].cnt=st[lid].cnt+st[rid].cnt-1;
	}else st[rt].cnt=st[lid].cnt+st[rid].cnt;
	st[rt].lc=st[lid].lc;
	st[rt].rc=st[rid].rc;
//	cout<<"%%"<<rt<<" "<<lid<<" "<<rid<<" "<<st[rt].lc<<" "<<st[rt].rc<<endl;	
}
inline Tree pushup(Tree x,Tree y)
{
	Tree z={0,0,0};
	if(!y.cnt)
	{
		z.l=x.l;z.r=x.r;
		z.cnt=x.cnt;
		z.lc=x.lc;
		z.rc=x.rc;
		return z;
	}
	if(!x.cnt)
	{
		z.l=y.l;z.r=y.r;
		z.cnt=y.cnt;
		z.lc=y.lc;
		z.rc=y.rc;
		return z;		
	}
	if(x.rc==y.lc)
	{
		z.cnt=x.cnt+y.cnt-1;
	}else z.cnt=x.cnt+y.cnt;
	z.lc=x.lc;
	z.rc=y.rc;
	z.l=x.l;z.r=y.r;
	return z;
}
inline void pushson(int rt,int col)
{
	if(st[lid].cnt)
	{
		st[lid].lz=st[lid].lc=st[lid].rc=col;
		st[lid].cnt=1;
	}
	if(st[rid].cnt)
	{
		st[rid].lz=st[rid].lc=st[rid].rc=col;
		st[rid].cnt=1;
	}	
}
inline void pushdown(int rt)
{
	if(st[rt].lz)
	{
		int lz=st[rt].lz;st[rt].lz=0;
		st[rt].cnt=1;
		pushson(rt,lz);
	}
}
//void pushcol(int rt,int col) {
//    st[rt].lc=st[rt].rc=col;
//    st[rt].cnt=1,st[rt].lz=col;
//}
//void pushdown(int rt) {
//    if(st[rt].lz) {
//        if(lid) pushcol(lid,st[rt].lz);
//        if(rid) pushcol(rid,st[rt].lz);
//        st[rt].lz=0;
//    }
//}
void bt(int rt,int l,int r)
{
	st[rt].l=l;st[rt].r=r;
	if(l==r)
	{
		st[rt].lc=st[rt].rc=w[rnk[l]];
		st[rt].cnt=1;
		return;
	}
	int mid=(l+r)>>1;
	bt(lid,l,mid);
	bt(rid,mid+1,r);
	st[rt]=pushup(st[lid],st[rid]);
//	_pushup(rt);
//	cout<<rt<<" "<<l<<" "<<r<<" "<<st[rt].cnt<<endl;
}
int rl,rr,pos1,pos2;
int query(int rt,int l,int r)
{

	if(l<=st[rt].l&&st[rt].r<=r)
	{
		if(st[rt].l==l)rl=st[rt].lc;
		if(st[rt].r==r)rr=st[rt].rc;
		return st[rt].cnt;
	}
	int mid=(st[rt].l+st[rt].r)>>1;
	int ans=0;
	pushdown(rt);
	if(r<=mid)return query(lid,l,r);
	if(l>mid)return query(rid,l,r);
	ans=query(lid,l,r)+query(rid,l,r);
//	if(l<=mid)ans+=query(lid,l,r);
//	if(r>mid)ans+=query(rid,l,r);
//	cout<<rt<<" "<<l<<" "<<r<<" "<<ans<<endl;
	if(l<=mid&&r>mid&&st[lid].rc==st[rid].lc)ans--;
	return ans;
}
void update(int rt,int l,int r,int val)
{
	if(l<=st[rt].l&&st[rt].r<=r)
	{
		st[rt].lc=st[rt].rc=val;
		st[rt].lz=val;
		st[rt].cnt=1;
//		pushcol(rt,val);
		return;
	}
	int mid=(st[rt].l+st[rt].r)>>1;
	pushdown(rt);
//	cout<<"##"<<rt<<" "<<st[rt].l<<" "<<st[rt].r<<" "<<st[rt].cnt<<endl;
	if(l<=mid)update(lid,l,r,val);
	if(r>mid) update(rid,l,r,val);
	st[rt]=pushup(st[lid],st[rid]);
//	_pushup(rt);
	return;	
}

void find(int u,int _fa,int depth)
{
	fa[u]=_fa;
	dep[u]=depth;
	son[u]=0;
	size[u]=1;
	int ms=0;
	for(int i=head[u];i;i=edge[i].next)
	{
		int to=edge[i].to;
		if(to==_fa)continue;
		if(dep[to])continue;
		find(to,u,depth+1);
		size[u]+=size[to];
		if(ms<size[to])
		{
			ms=size[to];
			son[u]=to;
		}
	}
}
void connect(int u,int acst)
{
	dfn[u]=++ntime;
	top[u]=acst;
	rnk[ntime]=u;
	if(son[u])
	{
		connect(son[u],acst);
	}
	for(int i=head[u];i;i=edge[i].next)
	{
		int to=edge[i].to;
		if(son[u]==to||to==fa[u])continue;
		connect(to,to);
	}
}
int Q(int l,int r)
{
	int ans=0;
	int tot=0;pos1=pos2=0;
	rl=rr=0;
	while(top[l]!=top[r])
	{
		if(dep[top[l]]<dep[top[r]])swap(l,r),swap(pos1,pos2);
		ans+=query(1,dfn[top[l]],dfn[l]);
		if(rr==pos1)tot++;
		l=fa[top[l]];pos1=rl;
	}
	if(dep[l]>dep[r])swap(l,r),swap(pos1,pos2);
	ans+=query(1,dfn[l],dfn[r]);
	if(rl==pos1)tot++;
	if(rr==pos2)tot++;
	return ans-tot;
}
void Qmodify(int l,int r,int c)
{
	while(top[l]!=top[r])
	{
		if(dep[top[l]]<dep[top[r]])swap(l,r);
//		cout<<"##"<<dfn[top[l]]<<" "<<dfn[l]<<endl;
		update(1,dfn[top[l]],dfn[l],c);
		l=fa[top[l]];
	}
	if(dep[l]>dep[r])swap(l,r);
	update(1,dfn[l],dfn[r],c);
}
int main()
{
//	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	cin>>n>>m;
	int a,b;
	for(int i=1;i<=n;i++)
	{
		cin>>w[i];
	}
	for(int i=1;i<n;i++)
	{
		cin>>a>>b;
		add(a,b);
		add(b,a);
	}
	find(1,0,1);
	connect(1,1);
	bt(1,1,n);
	int q;
	char op[5];
	for(int i=1;i<=m;i++)
	{
		cin>>op;
		if(op[0]=='C')
		{
			cin>>a>>b>>q;
			Qmodify(a,b,q);
		}else if(op[0]=='Q')
		{
			cin>>a>>b;
			cout<<Q(a,b)<<endl;			
		}
	}
	return 0;
}
posted @ 2024-05-17 15:57  wlesq  阅读(23)  评论(0编辑  收藏  举报