天天看点

codeforces 208 E 时间戳 倍增法求LCA

链接:http://www.codeforces.com/problemset/problem/208/E

题意:给你一个森林,m个询问:v,p

求有多少个点(除v外) 与 v的第p个祖先相同

这个题首先要解决找某个点的第p个祖先的问题,可以采用倍增法

记录一个二维数组p[u][i]表示u的第2^i个祖先,那么通过这个数组我们就可以知道u的上面任意深度(相对于u)祖先是谁(巧妙的利用二进制)

具体求法和用法见下,还可以找LCA的

/*
2^17=131072;
2^18=262144;
*/
const int POW = 18;
void dfs(int u,int fa){
	d[u]=d[fa]+1;
    p[u][0]=fa;
	for(int i=1;i<POW;i++) p[u][i]=p[p[u][i-1]][i-1];
	int sz=edge[u].size();
	for(int i=0;i<sz;i++){
		int v=edge[u][i];
		if(v==fa) continue;
		dfs(v,u);
	}
}
int lca( int a, int b ){
	if( d[a] > d[b] ) a ^= b, b ^= a, a ^= b;
	if( d[a] < d[b] ){
		int del = d[b] - d[a];
		for( int i = 0; i < POW; i++ ) if(del&(1<<i)) b=p[b][i];
	}
	if( a != b ){
		for( int i = POW-1; i >= 0; i-- ) 
			if( p[a][i] != p[b][i] ) 
			     a = p[a][i] , b = p[b][i];
		a = p[a][0], b = p[b][0];
	}
	return a;
}
           

这道题的话需要记录  每个点的两个时间戳,每个深度的所有时间戳

假设vp是v的p祖先,那么v 的 p  Cousins就是时间戳位于vp的两个时间戳之间深度为dep【v】

的点的个数

#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
const int maxn = 100010;
vector<int> g[maxn];
vector<int> edge[maxn];
vector<int> rt;
bool vis[maxn];
int n,m,dfn;
int in[maxn],out[maxn];
int dep[maxn],p[maxn][18];
int depth;
void dfs(int u,int fa){
	vis[u]=true;
	in[u]=dfn++;
//printf("in[%d]=%d\n",u,in[u]);
	dep[u]=dep[fa]+1;
	g[dep[u]].push_back(dfn);
    p[u][0]=fa;
	for(int i=1;i<18;i++) p[u][i]=p[p[u][i-1]][i-1];
	int sz=edge[u].size();
	for(int i=0;i<sz;i++){
		int v=edge[u][i];
		if(v==fa) continue;
		dfs(v,u);
	}
	out[u]=dfn++;
//printf("out[%d]=%d\n",u,out[u]);
}
int solve(int tfn,int d){
	int l=0,r=g[d].size()-1,best=-1;
	while(l<=r){
		int m=l+r>>1;
		if(g[d][m]<=tfn){
			l=m+1;  best=m;
		}
		else r=m-1;
	}
	return best+1;
}
int find(int v,int pp){
	for(int i=0;i<18;i++) if(pp&(1<<i)) v=p[v][i];
	return v;
}
int ans[maxn];
int main()
{
	while(scanf("%d",&n)!=EOF)
	{
		int fa,v,pp;
		rt.clear();
		memset(p,0,sizeof(p));
		memset(dep,0,sizeof(dep));
		for(int i=0;i<=n;i++) edge[i].clear(),g[i].clear();
		for(int i=1;i<=n;i++)
		{
			scanf("%d",&fa);
			if(fa==0) rt.push_back(i);
			else edge[fa].push_back(i);
		}
		dfn=0;depth=0;
		for(int i=0;i<rt.size();i++)  depth=0,dfs(rt[i],0);
		scanf("%d",&m);
		for(int i=0;i<m;i++)
		{
			scanf("%d%d",&v,&pp);
			int vp=find(v,pp);
			if(!vp) ans[i]=0;
			else {
			    ans[i]=solve(out[vp],dep[v])-solve(in[vp]-1,dep[v])-1;
			}
		}
		for(int i=0;i<m;i++) printf("%d ",ans[i]);
		puts("");

	}
	return 0;
}