P2486 [SDOI2011]染色
[SDOI2011]染色
题目描述
给定一棵
- 将节点
到节点 的路径上的所有点(包括 和 )都染成颜色 。 - 询问节点
到节点 的路径上的颜色段数量。
颜色段的定义是极长的连续相同颜色被认为是一段。例如 112221
由三段组成:11
、222
、1
。
输入格式
输入的第一行是用空格隔开的两个整数,分别代表树的节点个数
第二行有
第
第
每行首先有一个字符
- 若
为C
,则代表本次操作是一次染色操作,在一个空格后有三个用空格隔开的整数 ,代表将 到 的路径上所有点都染成颜色 。 - 若
为Q
,则代表本次操作是一次查询操作,在一个空格后有两个用空格隔开的整数 ,表示查询 到 路径上的颜色段数量。
输出格式
对于每次查询操作,输出一行一个整数代表答案。
样例 #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
提示
数据规模与约定
对于 C
或 Q
,保证给出的图是一棵树。
除原数据外,还存在一组不计分的 hack 数据。
Solution
题目要求要一种能够实现树上路径推平,路径统计的数据结构。首先看到与树上路径相关的数据结构的话可以先想到对这棵树进行树链剖分,这样就可以将树上路径问题转化为普通的序列问题。然后又看到了有推平操作,就想试试用珂朵莉树来写。
将树进行树链剖分,然后用珂朵莉树来维护。那么对于修改操作就很简单,直接对应推平即可:
struct NODE{
int l,r;
mutable int v;
NODE (int l,int r=0,int v=0) : l(l),r(r),v(v) {}
bool operator< (const NODE &a) const {return l<a.l;}
NODE operator+ (const NODE &a) const {return NODE(l?l:a.l,a.r?a.r:r,v+a.v-(r==a.l));}
};
set <NODE> ctlt;
auto split(int pos)
{
auto it=ctlt.lower_bound(NODE(pos));
if (it!=ctlt.end() && it->l==pos) return it;
it--;
if (it->r<pos) return ctlt.end();
int l=it->l,r=it->r,v=it->v;
ctlt.erase(it);
ctlt.insert(NODE(l,pos-1,v));
return ctlt.insert(NODE(pos,r,v)).first;
}
void assign(int l,int r,int x)
{
auto itr=split(r+1),itl=split(l);
ctlt.erase(itl,itr);
ctlt.insert(NODE(l,r,x));
}//珂朵莉树模板
void modifyl(int x,int y,int k)//常规树剖
{
while (top[x]!=top[y])
{
if (dep[top[x]]<dep[top[y]]) swap(x,y);
assign(id[top[x]],id[x],k);//将dfs序上的这一段推平
x=fa[top[x]];
}
if (dep[x]>dep[y]) swap(x,y);
assign(id[x],id[y],k);
}
这道题最难的地方就在于如何求路径上的颜色个数。因为树已经被我们剖分成了链,那么需要注意相邻的两条树链并不是互不影响,所以不能单纯的将两条链上的个数相加,因为可能存在两条链的交界处颜色相同,所以需要进行判断。这里重载了
NODE operator+ (const NODE &a) const {return NODE(l?l:a.l,a.r?a.r:r,v+a.v-(r==a.l));}//在结构体中定义的
//在用到NODE中的 + 时,此时的NODE就不是原来珂朵莉树中的NODE的含义,而是表示记录答案的结构体
//这个结构体中l表示左端颜色,r表示右端颜色,v表示这段颜色的总个数
NODE query(int l,int r)
{
auto itr=split(r+1),itl=split(l);
NODE res=NODE(0);//res用于记录[l,r]的答案属性
for (auto it=itl;it!=itr;it++)
res=res+NODE(it->v,it->v,1);//直接将新的这一个区间的属性并入res中
//需要注意的是重载后的+并不符合交换律,所以一定要注意先后顺序
return res;
}
int queryl(int x,int y)
{
NODE left=NODE(0),right=NODE(0);//left对应x,right对应y
while (top[x]!=top[y])
{
if (dep[top[x]]>dep[top[y]]) {left=query(id[top[x]],id[x])+left;x=fa[top[x]];}
else {right=query(id[top[y]],id[y])+right;y=fa[top[y]];}
}
if (dep[x]>dep[y]) left=query(id[y],id[x])+left;
else right=query(id[x],id[y])+right;
swap(left.l,left.r);//将left链的左右颠倒,方便与right判重
return (left+right).v;//返回答案
}
虽然题目上说添加了一组卡珂朵莉树的 Hack 数据,但是树链剖分+珂朵莉树这种做法吸个氧还是可以跑过的,Hack 数据用时
Code
既然上面注释都写的差不多了,那这里我就不写了(逃
#include<bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof a)
using namespace std;
template<typename T> void read(T &k)
{
k=0;T flag=1;char b=getchar();
while (!isdigit(b)) {flag=(b=='-')?-1:1;b=getchar();}
while (isdigit(b)) {k=k*10+b-48;b=getchar();}
k*=flag;
}
const int _SIZE=1e5;
int n,m;
int a[_SIZE+5];
struct EDGE{
int nxt,to;
}edge[(_SIZE<<1)+5];
int tot,head[_SIZE+5];
void AddEdge(int x,int y)
{
++tot;
edge[tot].nxt=head[x];
edge[tot].to=y;
head[x]=tot;
}
int son[_SIZE+5],dep[_SIZE+5],fa[_SIZE+5],col[_SIZE+5];
int siz[_SIZE+5],top[_SIZE+5],id[_SIZE+5],cnt;
void dfs1(int x,int F,int depth)
{
dep[x]=depth;
fa[x]=F;
siz[x]=1;
int maxson=-1;
for (int i=head[x];i;i=edge[i].nxt)
{
int twd=edge[i].to;
if (twd==F) continue;
dfs1(twd,x,depth+1);
siz[x]+=siz[twd];
if (siz[twd]>maxson) son[x]=twd,maxson=siz[twd];
}
}
void dfs2(int x,int topf)
{
id[x]=++cnt;
col[cnt]=a[x];
top[x]=topf;
if (!son[x]) return;
dfs2(son[x],topf);
for (int i=head[x];i;i=edge[i].nxt)
{
int twd=edge[i].to;
if (twd==son[x] || twd==fa[x]) continue;
dfs2(twd,twd);
}
}
struct NODE{
int l,r;
mutable int v;
NODE (int l,int r=0,int v=0) : l(l),r(r),v(v) {}
bool operator< (const NODE &a) const {return l<a.l;}
NODE operator+ (const NODE &a) const {return NODE(l?l:a.l,a.r?a.r:r,v+a.v-(r==a.l));}
};
set <NODE> ctlt;
auto split(int pos)
{
auto it=ctlt.lower_bound(NODE(pos));
if (it!=ctlt.end() && it->l==pos) return it;
it--;
if (it->r<pos) return ctlt.end();
int l=it->l,r=it->r,v=it->v;
ctlt.erase(it);
ctlt.insert(NODE(l,pos-1,v));
return ctlt.insert(NODE(pos,r,v)).first;
}
void assign(int l,int r,int x)
{
auto itr=split(r+1),itl=split(l);
ctlt.erase(itl,itr);
ctlt.insert(NODE(l,r,x));
}
NODE query(int l,int r)
{
auto itr=split(r+1),itl=split(l);
NODE res=NODE(0);
for (auto it=itl;it!=itr;it++)
res=res+NODE(it->v,it->v,1);
return res;
}
void modifyl(int x,int y,int k)
{
while (top[x]!=top[y])
{
if (dep[top[x]]<dep[top[y]]) swap(x,y);
assign(id[top[x]],id[x],k);
x=fa[top[x]];
}
if (dep[x]>dep[y]) swap(x,y);
assign(id[x],id[y],k);
}
int queryl(int x,int y)
{
NODE left=NODE(0),right=NODE(0);
while (top[x]!=top[y])
{
if (dep[top[x]]>dep[top[y]]) {left=query(id[top[x]],id[x])+left;x=fa[top[x]];}
else {right=query(id[top[y]],id[y])+right;y=fa[top[y]];}
}
if (dep[x]>dep[y]) left=query(id[y],id[x])+left;
else right=query(id[x],id[y])+right;
swap(left.l,left.r);
return (left+right).v;
}
int main()
{
//freopen("P2486.in","r",stdin);
read(n),read(m);
for (int i=1;i<=n;i++) read(a[i]);
for (int i=1;i<n;i++)
{
int u,v;
read(u),read(v);
AddEdge(u,v),AddEdge(v,u);
}
dfs1(1,0,1);
dfs2(1,1);
int len=1,last=col[1];
for (int i=2;i<=n;i++)
if (col[i]!=last)
ctlt.insert(NODE(i-len,i-1,last)),len=1,last=col[i];
else len++;
ctlt.insert(NODE(n+1-len,n,last));
ctlt.insert(NODE(n+1,n+3,0));
for (int i=1;i<=m;i++)
{
char op[2];scanf("%s",op);
if (op[0]=='C')
{
int u,v,k;
read(u),read(v),read(k);
modifyl(u,v,k);
}
else
{
int u,v;
read(u),read(v);
printf("%d\n",queryl(u,v));
}
}
return 0;
}
分类:
标签:
,
,
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步