[Codeforces 208E] Blood Cousins(dsu on tree+倍增)
[Codeforces 208E] Blood Cousins(dsu on tree+倍增)
题面
给出一个有根树森林,点集总大小为\(n\)。有\(m\)个询问,每个询问包含两个数\(v_i,p_i\),询问\(v_i\)的\(p_i\)级祖先的子树内有多少和\(v_i\)深度相同
分析
先把询问离线。
相同深度点的个数转化成:询问x子树内深度为k的个数-1.直接套dsu on tree板子,每个深度用一个变量计数。k级祖先用倍增求。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#define maxn 100000
#define maxlogn 20
using namespace std;
int n,m;
vector<int>E[maxn+5];
bool is_root[maxn+5];
inline void add_edge(int u,int v){
E[u].push_back(v);
E[v].push_back(u);
}
int deep[maxn+5];
int anc[maxn+5][maxlogn+5];
int fa[maxn+5],son[maxn+5],sz[maxn+5];
void dfs1(int x,int f){
sz[x]=1;
fa[x]=f;
anc[x][0]=f;
deep[x]=deep[f]+1;
for(int i=1;i<=maxlogn;i++) anc[x][i]=anc[anc[x][i-1]][i-1];
for(int y : E[x]){
if(y!=f){
dfs1(y,x);
sz[x]+=sz[y];
if(sz[y]>sz[son[x]]) son[x]=y;
}
}
}
inline int kth_anc(int x,int k){
for(int i=maxlogn;i>=0;i--){
if(k&(1<<i)) x=anc[x][i];
}
return x;
}
int cnt[maxn+5];
bool vis[maxn+5];
void calc(int x,int type){
cnt[deep[x]]+=type;
for(int y : E[x]){
if(!vis[y]&&y!=fa[x]) calc(y,type);
}
}
struct qtype{
int x;
int k;
int id;
qtype(){
}
qtype(int _x,int _k,int _id){
x=_x;
k=_k;
id=_id;
}
};
vector<qtype>q[maxn+5];
int ans[maxn+5];
void dfs2(int x,bool is_heavy){
for(int y : E[x]){
if(y!=fa[x]&&y!=son[x]){
dfs2(y,0);
}
}
if(son[x]){
dfs2(son[x],1);
vis[son[x]]=1;
}
calc(x,1);
vis[son[x]]=0;
for(qtype p : q[x]) ans[p.id]=cnt[deep[p.x]]-1;//记得减去自己
if(!is_heavy) calc(x,-1);
}
int main(){
int f,x,k;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&f);
if(f!=0) add_edge(f,i);
else is_root[i]=1;
}
for(int i=1;i<=n;i++) if(is_root[i]) dfs1(i,0);
scanf("%d",&m);
for(int i=1;i<=m;i++){
scanf("%d %d",&x,&k);
int z=kth_anc(x,k);
q[z].push_back(qtype(x,k,i));
}
for(int i=1;i<=n;i++) if(is_root[i]) dfs2(i,0);
for(int i=1;i<=m;i++) printf("%d ",ans[i]);
}
版权声明:因为我是蒟蒻,所以请大佬和神犇们不要转载(有坑)的文章,并指出问题,谢谢