Description
一棵 n 个点的树, 树根为 1.
一开始每个点上有一个 1…n 的颜色 ci, 不同点颜色可以相同.
现在有 q 次操作, 分为两种类型:
• 1 u l r: 询问子树 u 中有多少种在 l 到 r 之间的颜色至少出现了一次
• 2 u c: 将 u 的颜色修改为 c
对于前 20% 的数据, n, q ≤ 5000;
对于前 40% 的数据, n, q ≤ 50000;
对于另 20% 的数据, 没有修改操作;
对于另 20% 的数据, t = 0;
对于 100% 的数据, 1 ≤ n, q ≤ 10000
Solution
大数据结构题,有点恶心
首先单独考虑1种颜色,即x点涂上颜色后会对x至根的路径上所有点查询造成1的贡献,
而颜色相同dfs序相邻的两个点的lca至根的路径则被多算了一次因此要减去1。注意这里一定是dfs序相邻的才能满足,具体可以画一下图
多种颜色可以每种颜色开一个set维护他们的dfs序相邻,每次增加就修改前驱后继,删除同理,颜色区间就树状数组套一个线段树即可
实际操作中可以用单点修改+区间求和算一段子树的答案,比第一时间想到的树剖再单点查询好写
set确实好用,mark一下iterator和set重载运算符的技巧以后说不定经常用(flag)
Code
#include <stdio.h>
#include <string.h>
#include <algorithm>
#include <set>
#define rep(i,st,ed) for (int i=st;i<=ed;++i)
#define drp(i,st,ed) for (int i=st;i>=ed;--i)
#define lowbit(x) ((x)&(-(x)))
using std:: swap;
const int N=200005;
const int E=400005;
struct edge{int x,y,next;}e[E];
struct treeNode{int l,r,sum;}tree[N*81];
struct data{
int x,y;
friend bool operator <(data a,data b) {return a.y<b.y;}
} ymw;
std:: set<data> s[N];
std:: set<data>::iterator it;
int size[N],dep[N],pos[N],bl[N],fa[N][21],c[N],tmp[N],wer[N];
int root[N],cnt,n;
int ls[N],edCnt=0;
int read() {
int x=0,v=1; char ch=getchar();
for (;ch<'0'||ch>'9';v=(ch=='-')?(-1):(v),ch=getchar());
for (;ch<='9'&&ch>='0';x=x*10+ch-'0',ch=getchar());
return x*v;
}
void addEdge(int x,int y) {
e[++edCnt]=(edge){x,y,ls[x]}; ls[x]=edCnt;
e[++edCnt]=(edge){y,x,ls[y]}; ls[y]=edCnt;
}
void dfs(int now) {
pos[now]=++pos[0];
wer[pos[0]]=now;
rep(i,1,20) fa[now][i]=fa[fa[now][i-1]][i-1];
size[now]=1;
for (int i=ls[now];i;i=e[i].next) {
if (fa[now][0]==e[i].y) continue;
fa[e[i].y][0]=now;
dep[e[i].y]=dep[now]+1;
dfs(e[i].y);
size[now]+=size[e[i].y];
}
}
int get_lca(int x,int y) {
if (dep[x]<dep[y]) swap(x,y);
drp(i,20,0) if (dep[fa[x][i]]>=dep[y]) x=fa[x][i];
drp(i,20,0) if (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
if (x!=y) x=fa[x][0];
return x;
}
int query(int now,int tl,int tr,int l,int r) {
if (!now) return 0;
if (tl==l&&tr==r) return tree[now].sum;
int mid=(tl+tr)>>1;
if (r<=mid) return query(tree[now].l,tl,mid,l,r);
else if (l>mid) return query(tree[now].r,mid+1,tr,l,r);
else return query(tree[now].l,tl,mid,l,mid)+query(tree[now].r,mid+1,tr,mid+1,r);
}
void modify(int &now,int tl,int tr,int x,int v) {
if (!now) tree[now=++cnt]=(treeNode){0,0,0};
tree[now].sum+=v;
if (tl==tr) return ;
int mid=(tl+tr)>>1;
if (x<=mid) modify(tree[now].l,tl,mid,x,v);
else if (x>mid) modify(tree[now].r,mid+1,tr,x,v);
}
int get(int x,int l,int r) {
int ret=0;
while (x) {
ret+=query(root[x],1,n,l,r);
x-=lowbit(x);
}
return ret;
}
void add(int x,int y,int v) {
while (x<=n) {
modify(root[x],1,n,y,v);
x+=lowbit(x);
}
}
void ins(int x) {
int lower=0,upper=0;
ymw=(data){x,pos[x]};
s[c[x]].insert(ymw);
it=s[c[x]].find(ymw);
if (it!=s[c[x]].begin()) {it--; lower=(*it).x; it++;}
it++; if (it!=s[c[x]].end()) upper=(*it).x;
if (lower&&upper) add(c[x],pos[get_lca(lower,upper)],1);
if (lower) add(c[x],pos[get_lca(lower,x)],-1);
if (upper) add(c[x],pos[get_lca(upper,x)],-1);
add(c[x],pos[x],1);
}
void del(int x) {
int lower=0,upper=0;
ymw=(data){x,pos[x]};
it=s[c[x]].find(ymw);
if (it!=s[c[x]].begin()) {it--; lower=(*it).x; it++;}
it++; if (it!=s[c[x]].end()) upper=(*it).x;
if (lower&&upper) add(c[x],pos[get_lca(lower,upper)],-1);
if (lower) add(c[x],pos[get_lca(lower,x)],1);
if (upper) add(c[x],pos[get_lca(upper,x)],1);
add(c[x],pos[x],-1);
s[c[x]].erase(s[c[x]].find(ymw));
}
bool cmp(int x,int y) {return pos[x]<pos[y];}
int main(void) {
n=read(); int q=read(),wjp=read();
rep(i,1,n) c[i]=read(),tmp[i]=i,root[i]=++cnt;
rep(i,2,n) {
int x=read(),y=read();
addEdge(x,y);
}
dep[1]=1; dfs(1);
std:: sort(tmp+1,tmp+n+1,cmp);
rep(i,1,n) {
ins(tmp[i]);
}
int lastans=0;
while (q--) {
int opt=read(),x=read(),l=read();
if (opt==1) {
int r=read();
if (wjp) x^=lastans,l^=lastans,r^=lastans;
int getr=get(r,pos[x],pos[x]+size[x]-1);
int getl=get(l-1,pos[x],pos[x]+size[x]-1);
lastans=getr-getl;
printf("%d\n", getr-getl);
} else if (opt==2) {
if (wjp) x^=lastans,l^=lastans;
del(x);
c[x]=l;
ins(x);
}
}
return 0;
}