传送门
树链剖分一样就能看出来,更重要的是线段树的处理。
p数组与sum同步,.l/.r分别表示这个区间左端点和右端点的颜色。然后各种乱搞。
查询的时候,在链与链之间,如果颜色一样的话,则需要使当前答案-1。
一节微机课没搞出来,回去之后数学课想了一想,晚上终于调出来了。提交的时候行数200+,删去注释之后只有170+了。。。
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int max_n=1e5+5;
const int max_e=max_n*2;
const int max_N=max_n*4;
int n,m,x,y,N,u,t,ans;
int color[max_n],size[max_n],h[max_n],father[max_n],son[max_n];
int top[max_n],num[max_n],tree[max_n];
int point[max_n],next[max_e],v[max_e],tot;
int sum[max_N],delta[max_N],interval[max_n];
struct hp{
int l,r;
}p[max_N];
inline void add(int x,int y){
++tot; next[tot]=point[x]; point[x]=tot; v[tot]=y;
++tot; next[tot]=point[y]; point[y]=tot; v[tot]=x;
}
inline void dfs_1(int x,int fa,int dep){
size[x]=1; h[x]=dep; father[x]=fa;
int maxson=0;
for (int i=point[x];i;i=next[i])
if (v[i]!=fa){
dfs_1(v[i],x,dep+1);
size[x]+=size[v[i]];
if (size[v[i]]>maxson){
maxson=size[v[i]];
son[x]=v[i];
}
}
}
inline void dfs_2(int x,int fa){
if (son[fa]!=x) top[x]=x;
else top[x]=top[fa];
num[x]=++N;
if (son[x]) dfs_2(son[x],x);
for (int i=point[x];i;i=next[i])
if (v[i]!=fa&&v[i]!=son[x])
dfs_2(v[i],x);
}
inline void update(int now){
if (p[now<<1].r==p[now<<1|1].l)
sum[now]=sum[now<<1]+sum[now<<1|1]-1;
else sum[now]=sum[now<<1]+sum[now<<1|1];
p[now].l=p[now<<1].l;
p[now].r=p[now<<1|1].r;
}
inline void pushdown(int now,int l,int r,int mid){
if (delta[now]){
sum[now<<1]=1;
delta[now<<1]=delta[now];
p[now<<1].l=p[now<<1].r=delta[now];
sum[now<<1|1]=1;
delta[now<<1|1]=delta[now];
p[now<<1|1].l=p[now<<1|1].r=delta[now];
delta[now]=0;
}
}
inline void build(int now,int l,int r){
int mid=(l+r)>>1;
if (l==r){
interval[l]=now;
sum[now]=1;
p[now].l=p[now].r=color[tree[l]];
return;
}
build(now<<1,l,mid);
build(now<<1|1,mid+1,r);
update(now);
}
inline void interval_change(int now,int l,int r,int lrange,int rrange,int v){
int mid=(l+r)>>1;
if (lrange<=l&&r<=rrange){
sum[now]=1;
p[now].l=p[now].r=v;
delta[now]=v;
return;
}
pushdown(now,l,r,mid);
if (lrange<=mid)
interval_change(now<<1,l,mid,lrange,rrange,v);
if (mid+1<=rrange)
interval_change(now<<1|1,mid+1,r,lrange,rrange,v);
update(now);
}
inline int query(int now,int l,int r,int lrange,int rrange){
int mid=(l+r)>>1,ans=0;
if (lrange<=l&&r<=rrange) return sum[now];
pushdown(now,l,r,mid);
bool pd1=false,pd2=false;
if (lrange<=mid)
ans+=query(now<<1,l,mid,lrange,rrange),pd1=true;
if (mid+1<=rrange)
ans+=query(now<<1|1,mid+1,r,lrange,rrange),pd2=true;
if (pd1&&pd2&&p[now<<1].r==p[now<<1|1].l)
ans--;
return ans;
}
inline void CHANGE(int u,int t,int x){
int f1=top[u],f2=top[t];
while (f1!=f2){
if (h[f1]<h[f2]){
swap(u,t);
swap(f1,f2);
}
interval_change(1,1,N,num[f1],num[u],x);
u=father[f1];
f1=top[u];
}
if (num[u]>num[t]) swap(u,t);
interval_change(1,1,N,num[u],num[t],x);
}
inline int QUERY(int u,int t){
int f1=top[u],f2=top[t],ans=0;
while (f1!=f2){
if (h[f1]<h[f2]){
swap(u,t);
swap(f1,f2);
}
ans+=query(1,1,N,num[f1],num[u]);
//pushdown
int k=query(1,1,N,num[f1],num[f1]);
k=query(1,1,N,num[father[f1]],num[father[f1]]);
if (p[interval[num[f1]]].l==p[interval[num[father[f1]]]].l)
ans--;
u=father[f1];
f1=top[u];
}
if (num[u]>num[t]) swap(u,t);
ans+=query(1,1,N,num[u],num[t]);
return ans;
}
int main(){
scanf("%d%d",&n,&m);
for (int i=1;i<=n;++i)
scanf("%d",&color[i]);
for (int i=1;i<n;++i){
scanf("%d%d",&x,&y);
add(x,y);
}
dfs_1(1,0,1);
dfs_2(1,0);
for (int i=1;i<=n;++i)
tree[num[i]]=i;
build(1,1,N);
for (int i=1;i<=m;++i){
char ch=getchar();
while (ch!='C'&&ch!='Q') ch=getchar();
if (ch=='C'){
scanf("%d%d%d",&u,&t,&x);
CHANGE(u,t,x);
}
else{
scanf("%d%d",&u,&t);
ans=QUERY(u,t);
printf("%d\n",ans);
}
}
}
①关于线段树的任何操作(包括有时候乱搞的直接查询编号),都要思考一下有没有pushdown,即当前也许只是打了标记,没有更新到最新值。
②自己写input不要写错了。