[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)\) 行,每行描述一个操作,其格式为:
每行首先有一个字符 \(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\) 一定为 C
或 Q
,保证给出的图是一棵树。
除原数据外,还存在一组不计分的 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=1,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;
}