P2486 [SDOI2011]染色
题目链接
P2486 [SDOI2011]染色
给定一棵 \(n\) 个节点的无根树,共有 \(m\) 个操作,操作分为两种:
- 将节点 \(a\) 到节点 \(b\) 的路径上的所有点 (包括 \(a\) 和 \(b\) ) 都染成颜色 \(c\) 。
- 询问节点 \(a\) 到节点 \(b\) 的路径上的颜色段数量。
颜色段的定义是极长的连续相同颜色被认为是一段。例如 112221 由三段组成:11
、 222
、 1
。
输入格式
输入的第一行是用空格隔开的两个整数,分别代表树的节点个数 \(n\) 和操作个数 \(m\) 。
第二行有 \(n\) 个用空格隔开的整数,第 \(i\) 个整数 \(w_{i}\) 代表结点 \(i\) 的初始颜色。
第 \(3\) 到第 \((n+1)\) 行,每行两个用空格隔开的整数 \(u, v\) ,代表树上存在一条连结节点 \(u\) 和节点 \(v\) 的边。
第 \((n+2)\) 到第 \((n+m+1)\) 行,每行描述一个操作,其格式为:
每行首先有一个字符 \(o p\) ,代表本次操作的类型。
- 若 \(o p\) 为
C
,则代表本次操作是一次染色操作,在一个空格后有三个用空格隔开的整数 \(a, b, c\) ,代表将 \(a\) 到 \(b\) 的路径上所有点都染成颜色 \(c_{\text {。 }}\) - 若 \(o p\) 为
Q
,则代表本次操作是一次查询操作,在一个空格后有两个用空格隔开的整数 \(a, b\) ,表示查询 \(a\) 到 \(b\) 路径上的颜色段数量。
输出格式
对于每次查询操作,输出一行一个整数代表答案。
输入输出样例
输入
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
输出
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\)一定为 \(\mathrm{C}\) 或 \(Q\) , 保证给出的图是一棵树。
解题思路
树链剖分
利用树链剖分思想将问题转换为一段连续区间上的操作,即统计区间上颜色段的数量,对于一段区间来说,先分为左右部分,统计左右部分颜色段的数量之和,如果左部分和右部分中间的颜色一样则说明该颜色段重复计算了,减一即可,所以线段树中应该存储的信息有 区间颜色段
,区间左边颜色
,区间右边颜色
,但由于自上而下更新时如果当前区间完全在修改区间内,这时会直接返回,下面范围更小的区间还没有更新,所以需要打懒标记。另外树链剖分形成的区间可能是多段,段与段之间也可能出现相同颜色,这时也会重复,查询时记录两边相邻的颜色,最后处理哪一段判断相应的颜色是否相等即可
- 时间复杂度:\(O((n+m)\times logn)\)
代码
// Problem: P2486 [SDOI2011]染色
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P2486
// Memory Limit: 125 MB
// Time Limit: 1000 ms
//
// Powered by CP Editor (https://cpeditor.org)
// %%%Skyqwq
#include <bits/stdc++.h>
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template <typename T> void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
const int N=1e5+5;
int n,m,c[N],nc[N],id[N],cnt,sz[N],fa[N],dep[N],son[N],top[N];
vector<int> adj[N];
struct T
{
int l,r;
int lc,rc,sum,col;
}tr[N<<2];
void pushup(int u)
{
tr[u].sum=tr[u<<1].sum+tr[u<<1|1].sum;
if(tr[u<<1].rc==tr[u<<1|1].lc)tr[u].sum--;
tr[u].lc=tr[u<<1].lc;
tr[u].rc=tr[u<<1|1].rc;
}
void pushdown(int u)
{
if(tr[u].col)
{
tr[u<<1].lc=tr[u<<1].rc=tr[u<<1|1].lc=tr[u<<1|1].rc=tr[u].col;
tr[u<<1].col=tr[u<<1|1].col=tr[u].col;
tr[u<<1].sum=tr[u<<1|1].sum=1;
tr[u].col=0;
}
}
void build(int u,int l,int r)
{
tr[u]={l,r};
if(l==r)
{
tr[u].lc=tr[u].rc=nc[l];
tr[u].sum=1;
return ;
}
int mid=l+r>>1;
build(u<<1,l,mid);
build(u<<1|1,mid+1,r);
pushup(u);
}
void update(int u,int l,int r,int d)
{
if(l<=tr[u].l&&tr[u].r<=r)
{
tr[u].lc=tr[u].rc=tr[u].col=d;
tr[u].sum=1;
return ;
}
pushdown(u);
int mid=tr[u].l+tr[u].r>>1;
if(l<=mid)update(u<<1,l,r,d);
if(r>mid)update(u<<1|1,l,r,d);
pushup(u);
}
T ask(int u,int l,int r)
{
if(l<=tr[u].l&&tr[u].r<=r)return tr[u];
int mid=tr[u].l+tr[u].r>>1;
pushdown(u);
if(l<=mid&&r>mid)
{
T res;
T L=ask(u<<1,l,r),R=ask(u<<1|1,l,r);
res.sum=L.sum+R.sum;
res.lc=L.lc,res.rc=R.rc;
if(L.rc==R.lc)
res.sum--;
return res;
}
if(l<=mid)return ask(u<<1,l,r);
return ask(u<<1|1,l,r);
}
void update_path(int x,int y,int d)
{
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]])swap(x,y);
update(1,id[top[x]],id[x],d);
x=fa[top[x]];
}
if(dep[x]<dep[y])swap(x,y);
update(1,id[y],id[x],d);
}
int ask_path(int x,int y)
{
int res=0,lx=0,ly=0;
while(top[x]!=top[y])
{
if(dep[top[x]]<dep[top[y]])swap(x,y),swap(lx,ly);
T t=ask(1,id[top[x]],id[x]);
res+=t.sum;
if(t.rc==lx)res--;
lx=t.lc;
x=fa[top[x]];
}
if(dep[x]>dep[y])swap(x,y),swap(lx,ly);
T t=ask(1,id[x],id[y]);
res+=t.sum;
if(t.lc==lx)res--;
if(t.rc==ly)res--;
return res;
}
void dfs1(int x,int father,int depth)
{
fa[x]=father,sz[x]=1,dep[x]=depth;
for(int y:adj[x])
{
if(y==father)continue;
dfs1(y,x,depth+1);
sz[x]+=sz[y];
if(sz[son[x]]<sz[y])son[x]=y;
}
}
void dfs2(int x,int t)
{
id[x]=++cnt,nc[cnt]=c[x],top[x]=t;
if(!son[x])return ;
dfs2(son[x],t);
for(int y:adj[x])
{
if(y==fa[x]||y==son[x])continue;
dfs2(y,y);
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&c[i]);
for(int i=1;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
adj[x].pb(y),adj[y].pb(x);
}
dfs1(1,1,1);
dfs2(1,1);
build(1,1,n);
while(m--)
{
char op[2];
int a,b,c;
scanf("%s%d%d",op,&a,&b);
if(*op=='C')
{
scanf("%d",&c);
update_path(a,b,c);
}
else
printf("%d\n",ask_path(a,b));
}
return 0;
}