题目:
给定一棵有n个节点的无根树和m个操作,操作有2类:
1、将节点a到节点b路径上所有点都染成颜色c;
2、询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如“ 112221 ” 由3段组成:“ 11 ” 、“ 222 ” 和“ 1 ” 。
请你写一个程序依次完成这m个操作。
Input
第一行包含2个整数n和m,分别表示节点数和操作数;
第二行包含n个正整数表示n个节点的初始颜色
下面 行每行包含两个整数x和y,表示x和y之间有一条无向边。
下面 行每行描述一个操作:
“C a b c”表示这是一个染色操作,把节点a到节点b路径上所有点(包括a和b)都染成颜色c;
“Q a b”表示这是一个询问操作,询问节点a到节点b(包括a和b)路径上的颜色段数量。
Output 对于每个询问操作,输出一行答案。
思路:这道题可以用树链剖分写,我用LCT写的,节点维护一下区间的左颜色和右颜色就可以了
代码:
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<string>
#include<vector>
#include<map>
#include<set>
#include<queue>
#include<stack>
#include<list>
#include<numeric>
using namespace std;
#define LL long long
#define ULL unsigned long long
#define INF 0x3f3f3f3f
#define mm(a,b) memset(a,b,sizeof(a))
#define PP puts("*********************");
template<class T> T f_abs(T a){ return a > 0 ? a : -a; }
template<class T> T gcd(T a, T b){ return b ? gcd(b, a%b) : a; }
template<class T> T lcm(T a,T b){return a/gcd(a,b)*b;}
// 0x3f3f3f3f3f3f3f3f
// 0x3f3f3f3f
const int maxn=3e5+50;
const int maxe=2*maxn;
struct Edge{
int to,next;
}edge[maxe];
int head[maxn],tot;
int ch[maxn][2],key[maxn],pre[maxn],rev[maxn];
int add[maxn],L[maxn],R[maxn],sum[maxn];
bool rt[maxn];//标记节点是不是splay的根
int n;//n个节点,从1开始计数
void init(){
tot=0;
for(int i=0;i<=n;i++){
head[i]=-1;
rt[i]=true;
ch[i][0]=ch[i][1]=0;
pre[i]=0;
rev[i]=0;
add[i]=-1;
}
L[0]=R[0]=key[0]=add[0]=-1;
sum[0]=0;
}
void addedge(int u,int v){
edge[tot].to=v;
edge[tot].next=head[u];
head[u]=tot++;
}
void dfs(int u,int fa){
pre[u]=fa;
L[u]=R[u]=key[u];
sum[u]=1;
for(int i=head[u];i!=-1;i=edge[i].next){
int v=edge[i].to;
if(v!=fa)
dfs(v,u);
}
}
void update_add(int x,int val){
if(!x) return;
L[x]=R[x]=key[x]=add[x]=val;
sum[x]=1;
}
void update_rev(int x){
if(!x) return;
swap(L[x],R[x]);
swap(ch[x][0],ch[x][1]);
rev[x]^=1;
}
void push_down(int x){
if(add[x]!=-1){
update_add(ch[x][0],add[x]);
update_add(ch[x][1],add[x]);
add[x]=-1;
}
if(rev[x]){
update_rev(ch[x][0]);
update_rev(ch[x][1]);
rev[x]=0;
}
}
void push_up(int x){
L[x]=R[x]=key[x];
add[x]=-1;
sum[x]=1;
int ls=ch[x][0],rs=ch[x][1];
if(ls!=0){
sum[x]+=sum[ls];
if(R[ls]==L[x]) sum[x]--;
L[x]=L[ls];
}
if(rs!=0){
sum[x]+=sum[rs];
if(R[x]==L[rs]) sum[x]--;
R[x]=R[rs];
}
}
void rotate(int x){
int y=pre[x],d=ch[y][1]==x;
ch[y][d]=ch[x][!d];
pre[ch[y][d]]=y;
pre[x]=pre[y];
pre[y]=x;
ch[x][!d]=y;
if(rt[y]) rt[y]=false,rt[x]=true;
else ch[pre[x]][ch[pre[x]][1]==y]=x;
push_up(y);
}
//P函数先将根结点到x的路径上所有的结点的标记逐级下放
void P(int x){
if(!rt[x]) P(pre[x]);
push_down(x);
}
//将x旋转到根
void splay(int x){
P(x);
while(!rt[x]){
int f=pre[x],ff=pre[f];
if(rt[f]) rotate(x);
else if((ch[ff][1]==f)==(ch[f][1]==x))
rotate(f),rotate(x);
else
rotate(x),rotate(x);
}
push_up(x);
}
//将x到根的路径变成首选边
int Access(int x){
int y=0;
for(;x;x=pre[y=x]){
splay(x);
rt[ch[x][1]]=true;
rt[ch[x][1]=y]=false;
push_up(x);
}
return y;
}
//让x成为它所在树的根
void make_root(int x){
Access(x);
splay(x);
update_rev(x);
}
int main(){
int m,a,b,c;
char str[20];
while(~scanf("%d%d",&n,&m)){
init();
for(int i=1;i<=n;i++)
scanf("%d",&key[i]);
for(int i=1;i<n;i++){
scanf("%d%d",&a,&b);
addedge(a,b);
addedge(b,a);
}
dfs(1,0);
while(m--){
scanf("%s",str);
if(str[0]=='C'){
scanf("%d%d%d",&a,&b,&c);
make_root(a);
Access(b);
splay(b);
update_add(b,c);
}
else{
scanf("%d%d",&a,&b);
make_root(a);
Access(b);
splay(b);
printf("%d\n",sum[b]);
}
}
}
return 0;
}