P2486 [SDOI2011]染色

题目链接

P2486 [SDOI2011]染色

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

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

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

输入格式

输入的第一行是用空格隔开的两个整数,分别代表树的节点个数 n 和操作个数 m
第二行有 n 个用空格隔开的整数,第 i 个整数 wi 代表结点 i 的初始颜色。
3 到第 (n+1) 行,每行两个用空格隔开的整数 u,v ,代表树上存在一条连结节点 u 和节点 v 的边。
(n+2) 到第 (n+m+1) 行,每行描述一个操作,其格式为:
每行首先有一个字符 op ,代表本次操作的类型。

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

输出格式

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

输入输出样例

输入

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% 的数据, 1n,m1051wi,c1091a,b,u,vnop一定为 CQ , 保证给出的图是一棵树。

解题思路

树链剖分

利用树链剖分思想将问题转换为一段连续区间上的操作,即统计区间上颜色段的数量,对于一段区间来说,先分为左右部分,统计左右部分颜色段的数量之和,如果左部分和右部分中间的颜色一样则说明该颜色段重复计算了,减一即可,所以线段树中应该存储的信息有 区间颜色段区间左边颜色区间右边颜色,但由于自上而下更新时如果当前区间完全在修改区间内,这时会直接返回,下面范围更小的区间还没有更新,所以需要打懒标记。另外树链剖分形成的区间可能是多段,段与段之间也可能出现相同颜色,这时也会重复,查询时记录两边相邻的颜色,最后处理哪一段判断相应的颜色是否相等即可

  • 时间复杂度:O((n+m)×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; }

__EOF__

本文作者acwing_zyy
本文链接https://www.cnblogs.com/zyyun/p/16249668.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zyy2001  阅读(36)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示