Blood Cousins CodeForces - 208E 离线+DSU/DFS

题目链接: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]);
}

猜你喜欢

转载自blog.csdn.net/qq_35320178/article/details/81395219