题目链接:CodeForce-208E
鉴于打这道题的时候深受DSU的影响,二话不说就打起了DSU,磕磕碰碰也就过了,后来发现不用DSU也可以,以下就两种都讲。
主要思路:①
初始问题为求与节点v第p个祖先相同的结点有多少个
将每个问题转化为当全局的集合里头只有结点V的第p个祖先及其子树的时候,深度与v相同的结点有多少个。
#include<cstdio>
#include<vector>
#define M 100005
using namespace std;
struct que {
int id,d;
};
vector<que>Q[M];
struct E {
int to,nx;
} edge[M];
int tot,head[M];
void Addedge(int a,int b) {
edge[++tot].to=b;
edge[tot].nx=head[a];
head[a]=tot;
}
int fa[M][20],T,L[M],R[M],dep[M],son[M],sz[M],P[M];
void dfs(int now) {
L[now]=++T;
P[T]=now;
son[now]=0;
sz[now]=1;
for(int i=head[now]; i; i=edge[i].nx) {
int nxt=edge[i].to;
dep[nxt]=dep[now]+1;
dfs(nxt);
if(sz[son[now]]<sz[nxt])son[now]=nxt;//处理出重儿子
sz[now]+=sz[nxt];
}
R[now]=T;
}
int cnt[M];
void Addpoint(int x,int d) {
int X=dep[x];
cnt[X]+=d;//当前层个数加1
}
void Addtree(int x,int d) {
for(int i=L[x]; i<=R[x]; i++) {
Addpoint(P[i],d);
}
}
int ans[M];
void solve(int now) {//DSU基本套路
for(int i=head[now]; i; i=edge[i].nx) {
int nxt=edge[i].to;
if(nxt==son[now])continue;
solve(nxt),Addtree(nxt,-1);
}
if(son[now])solve(son[now]);
for(int i=head[now]; i; i=edge[i].nx) {
int nxt=edge[i].to;
if(nxt==son[now])continue;
Addtree(nxt,1);
}
if(now==0)return;
Addpoint(now,1);
for(int i=0; i<Q[now].size(); i++) {
ans[Q[now][i].id]=cnt[Q[now][i].d]-1;//要减去他自己
}
}
int Up(int x,int y) {//这里利用倍增表快速求出其第y个祖先(不包括他自己)
for(int i=0; i<20; i++) {
if((1<<i)&y)x=fa[x][i];
}
return x;
}
void Initfa(int n) {//预处理ST表
for(int j=1; j<20; j++) {
for(int i=1; i<=n; i++)fa[i][j]=fa[fa[i][j-1]][j-1];
}
}
int main() {
int n,m;
scanf("%d",&n);
for(int i=1; i<=n; i++) {
scanf("%d",&fa[i][0]);
Addedge(fa[i][0],i);
}
dfs(0);
Initfa(n);
scanf("%d",&m);
for(int i=1; i<=m; i++) {
int v,p;
scanf("%d%d",&v,&p);
int nxt=Up(v,p);//求出其第p祖先
Q[nxt].push_back((que) {i,dep[v]});//添加问题
}
solve(0);
for(int i=1; i<m; i++)printf("%d ",ans[i]);
printf("%d\n",ans[m]);
}
主要思路②:
先dfs一遍,依次将其dfs序存入当前深度cnt数组中,求出v的第p祖先的子树中深度与v相等的结点个数(有点讲不清楚,见代码)。
#include<cstdio>
#include<vector>
#include<algorithm>
#define M 100005
using namespace std;
struct que {
int id,d;
};
vector<que>Q[M];
struct E {
int to,nx;
} edge[M];
int tot,head[M];
void Addedge(int a,int b) {
edge[++tot].to=b;
edge[tot].nx=head[a];
head[a]=tot;
}
int fa[M][20],T,L[M],R[M],dep[M],P[M];
vector<int>cnt[M];
void dfs(int now) {
L[now]=++T;
if(now)cnt[dep[now]].push_back(T);//存到其深度的cnt数组中
for(int i=head[now]; i; i=edge[i].nx) {
int nxt=edge[i].to;
dep[nxt]=dep[now]+1;
dfs(nxt);
}
R[now]=T;
}
int ans[M];
int Up(int x,int y) {
for(int i=0; i<20; i++) {
if((1<<i)&y)x=fa[x][i];
}
return x;
}
void Initfa(int n) {
for(int j=1; j<20; j++) {
for(int i=1; i<=n; i++)fa[i][j]=fa[fa[i][j-1]][j-1];
}
}
void solve(){
int D=1;
while(cnt[D].size()!=0){
for(int i=0;i<Q[D].size();i++){//因为dfs序存入时就有序,故不用sort
que nxt=Q[D][i];
int l=lower_bound(cnt[D].begin(),cnt[D].end(),L[nxt.d])-cnt[D].begin();//第一个大于其左端点的坐标
int r=upper_bound(cnt[D].begin(),cnt[D].end(),R[nxt.d])-cnt[D].begin()-1;//最后一个小于其右端点的坐标
ans[nxt.id]=r-l;//不是r-l+1,因为要减去它自己
}
D++;
}
}
int main() {
int n,m;
scanf("%d",&n);
for(int i=1; i<=n; i++) {
scanf("%d",&fa[i][0]);
Addedge(fa[i][0],i);
}
dfs(0);
Initfa(n);
scanf("%d",&m);
for(int i=1; i<=m; i++) {
int v,p;
scanf("%d%d",&v,&p);
int nxt=Up(v,p);//同样的 求出第p祖先
if(nxt==0)continue;
Q[dep[v]].push_back((que) {i,nxt});
}
solve();
for(int i=1; i<m; i++)printf("%d ",ans[i]);
printf("%d\n",ans[m]);
}