您的位置:首页 > 其它

codeforces 208 E 时间戳 倍增法求LCA

2014-11-20 22:31 375 查看
转:http://blog.csdn.net/crazy_ac/article/details/7796497

链接: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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: