题解:BZOJ#4771 七彩树
1.Descrption
给定一棵有 \(n\) 个节点的树,树上的每个节点有一种颜色,现在给定 \(m\) 个询问,形如 \((x,d)\),表示询问在以 \(x\) 为根的子树中,深度在 \([dep_x,dep_x+d]\) 之间的节点里,有多少种不同的颜色,询问强制在线。
2.Solution
我们首先考虑简化问题,如果将每个询问中的限制去掉,改为查询 \(x\) 的子树中有多少种不同的颜色的话应该怎么做?
首先我们得出 dfs 序,将树上问题转换为序列问题。
然后先将整棵树中的所有节点的贡献都视为 \(1\),但是我们考虑一对相同颜色的点 \(u,v\),在 \(u,v\) 的 \(LCA\) 及其祖先节点的询问中,\(u,v\) 的贡献将被计算为 \(2\),而实际贡献只为 \(1\),所以我们需要在 \(LCA\) 处减一。
由此,不妨假定颜色为 \(col\) 的节点共有 \(x\) 个,我们需要在原树上找到 \(x-1\) 个点(注意,这里找到的点颜色同样可以是 \(col\),并且可以重复),和这 \(x\) 个点构成一棵虚树,使得每一棵子树中的权值和都恰好为 \(1\),这显然可以将这 \(x\) 个点按照 dfs 序排序,然后在相邻两个点的 \(LCA\) 处减一,从而构造出来,可以使用线段树维护区间和,下面有深度限制时亦然。
但是这个时候有深度限制,我们不妨“一层一层”的考虑,首先考虑深度为 \(1\) 的点,然后考虑深度为 \(2\) 的点,一直将所有的点考虑完。
具体的,我们使用若干个 vector 储存对应深度的点有哪些,从小到大枚举深度,加入对应的点,这个时候需要使用 \(n\) 个 set 记录每一种颜色现在已经加入了哪些元素(这里需要按照 dfs 序排序)。
(注:下面这一段的'在......处'皆指在其对应的 dfs 序处进行操作。)
每一次加入一个节点 \(x\),在 set 中找到 \(x\) 的前驱 \(pre\) 和后继 \(suf\),显然在没有加入这个节点的时候,\(pre\) 和 \(suf\) 相邻,根据我们在没有深度限制的时候的分析,在 \(pre\) 和 \(suf\) 的 \(LCA\) 处被减一了,这个时候应该加回去,然后在 \(pre\) 和 \(x\) 的 \(LCA\) 和 \(suf\) 和 \(x\) 的 \(LCA\) 处减一,最后,在 \(x\) 处加一。
由于强制在线,我们可以使用可持久化线段树,对于一个询问 \((x,d)\),相当于在 \(dep_x+d\) 这个版本的线段树中查询。
注意在查询时判断 \(dep_x+d\) 的大小是否超过了这棵树的深度,也注意特判 \(x\) 没有前驱和后继的情况!
当然也得注意哪里使用的是点的编号,哪里使用的是点的 dfs 序。
3.Code
/*by qwer6*/
/*略去缺省源和快读快写*/
const int N=1e5+5,M=7e6+5;
int n,m,mxdepth;
int col[N];
int rt[N];//每一个深度对应的根
int dep[N];//树上节点深度
int cnt_dfn;// dfs 序
int L[N],R[N],dfn[N];
int fa[20][N];
vector<int>Idx[N];//深度为 i 的点的编号
set<int>Now[N];//Now[i] 当前已经加入的颜色为 i 的点的 dfs 序
struct Chain_forword_star{
struct Edge{
int v,nxt;
}e[N];
int n,cnt_edge;
int head[N];
void init(int _n){
n=_n,cnt_edge=0;
for(int i=1;i<=n;i++)head[i]=0;
}
void AddEdge(int u,int v){
e[++cnt_edge]={v,head[u]};
head[u]=cnt_edge;
}
}G;
struct Segment_tree{
int num;
int c[M],ls[M],rs[M];
#define mid (l+r>>1)
int New(){
num++;
c[num]=ls[num]=rs[num]=0;
return num;
}
int copy(int p){
int q=New();
c[q]=c[p],ls[q]=ls[p],rs[q]=rs[p];
return q;
}
void pushup(int p){
c[p]=c[ls[p]]+c[rs[p]];
}
int build(int l,int r){
int p=New();
if(l==r)return p;
ls[p]=build(l,mid),rs[p]=build(mid+1,r);
return p;
}
int change(int p,int l,int r,int x,int v){
int q=copy(p);
if(l==r){
c[q]+=v;
return q;
}
if(mid>=x)ls[q]=change(ls[p],l,mid,x,v);
else rs[q]=change(rs[p],mid+1,r,x,v);
pushup(q);
return q;
}
int query(int p,int l,int r,int L,int R){
if(L<=l&&r<=R)return c[p];
if(mid>=L&&mid<R)return query(ls[p],l,mid,L,R)+query(rs[p],mid+1,r,L,R);
if(mid>=L)return query(ls[p],l,mid,L,R);
return query(rs[p],mid+1,r,L,R);
}
}Set;
void init(){
mxdepth=1;
Set.num=cnt_dfn=0;
G.init(n);
for(int i=1;i<=n;i++){
Idx[i].clear();
Now[i].clear();
}
}
void dfs(int u){
tomax(mxdepth,dep[u]);
L[u]=++cnt_dfn;
dfn[cnt_dfn]=u;
for(int i=1;i<=17;i++)
fa[i][u]=fa[i-1][fa[i-1][u]];
for(int i=G.head[u],v;i;i=G.e[i].nxt){
v=G.e[i].v;
dep[v]=dep[u]+1;
dfs(v);
}
R[u]=cnt_dfn;
}
int LCA(int u,int v){
if(dep[u]<dep[v])swap(u,v);
int d=dep[u]-dep[v];
for(int i=0;i<=17;i++)
if(d&1<<i)u=fa[i][u];
if(u==v)return u;
for(int i=17;i>=0;i--)
if(fa[i][u]!=fa[i][v])
u=fa[i][u],v=fa[i][v];
return fa[0][u];
}
int find_pre(int x,int bel){
auto it=Now[bel].lower_bound(x);
if(it==Now[bel].begin())return -1;
return *(--it);
}
int find_suf(int x,int bel){
auto it=Now[bel].upper_bound(x);
if(it==Now[bel].end())return -1;
return *it;
}
signed main(){
int t;
read(t);
while(t--){
read(n),read(m);
init();
for(int i=1;i<=n;i++)read(col[i]);
for(int i=2;i<=n;i++){
read(fa[0][i]);
G.AddEdge(fa[0][i],i);
}
dep[1]=1;
dfs(1);
//Idx 中存的是点的编号
//Now 中存的是点对应的 dfs 序
//LCA 接受的参数是点的编号
//Set 中点对应的下标是 dfs 序
//L[u] 表示编号 u 对应的 dfs 序
//dfn[x] 表示 dfs 序 x 对应的点的编号
for(int i=1;i<=n;i++)
Idx[dep[i]].push_back(i);
rt[0]=Set.build(1,n);
for(int i=1,pre,suf;i<=mxdepth;i++){
rt[i]=rt[i-1];
for(int x:Idx[i]){
rt[i]=Set.change(rt[i],1,n,L[x],1);
pre=find_pre(L[x],col[x]);
suf=find_suf(L[x],col[x]);
if(pre!=-1&&suf!=-1)
rt[i]=Set.change(rt[i],1,n,L[LCA(dfn[pre],dfn[suf])],1);
if(pre!=-1)
rt[i]=Set.change(rt[i],1,n,L[LCA(dfn[pre],x)],-1);
if(suf!=-1)
rt[i]=Set.change(rt[i],1,n,L[LCA(x,dfn[suf])],-1);
Now[col[x]].insert(L[x]);
}
}
for(int i=1,last=0,x,d;i<=m;i++){
x=read()^last,d=read()^last;
d=min(mxdepth,dep[x]+d);
last=Set.query(rt[d],1,n,L[x],R[x]);
write(last),Nxt;
}
}
}

浙公网安备 33010602011771号