CodeForces 208E Blood Cousins(树剖求lca + dfs序)

题意:
给出若干棵树,q次询问,每次询问某结点n向上k步的父节点的子树中有多少个和n是兄弟结点(深度相同)
ps:这里的题意是将问题简化过后的
假设现在只有一棵树,首先考虑结点的子树如何确定,dfs序可以解决,再考虑向上k步的lca如何解决,倍增法求lca(弱不会),由于只会树剖向上爬,外加上树剖可以直接处理dfs序,所以就写树剖了…对于子树内符合条件的结点,只需要开 105 个深度的vector存一下dfs序,然后每次lower_bound和upper_bound就能找到对应答案…做法没什么不对,就是从来没这样搞过,好在1A,codeforces题目还是神奇…
仔细想想这不就是在XJB搞嘛…..

#include<cstring>
#include<string>
#include<iostream>
#include<queue>
#include<cstdio>
#include<algorithm>
#include<map>
#include<cstdlib>
#include<cmath>
#include<vector>
//#pragma comment(linker, "/STACK:1024000000,1024000000");

using namespace std;

#define INF 0x3f3f3f3f
#define maxn 200003

int a[maxn];

int fir[maxn],nex[maxn],e_max,v[maxn];
int in[maxn],out[maxn];
int son[maxn],fa[maxn],deep[maxn],top[maxn],siz[maxn],cnt,tot;
int inv[maxn];

vector<int>vec[100005];

void init_()
{
    memset(fir,-1,sizeof fir);
    memset(son,-1,sizeof son);
    e_max=0;
    cnt=0;
    tot=0;
}

void add_edge(int s,int t)
{
    int e=e_max++;
    v[e]=t;
    nex[e]=fir[s];
    fir[s]=e;
}

void dfs1(int k,int pre,int d)
{
    deep[k]=d;
    fa[k]=pre;
    siz[k]=1;

    for(int i=fir[k];~i;i=nex[i])
    {
        int e=v[i];
        if(e!=pre)
        {
            dfs1(e,k,d+1);
            siz[k]+=siz[e];
            if(son[k]==-1||siz[son[k]]<siz[e]) son[k]=e;
        }
    }
}

void dfs2(int k,int sp)
{
    top[k]=sp;
    in[k]=++tot;
    inv[tot]=k;
    vec[deep[k]].push_back(in[k]);
    out[k]=in[k];
    if(son[k]==-1) return;
    dfs2(son[k],sp);
    out[k]=max(out[k],out[son[k]]);
    for(int i=fir[k];~i;i=nex[i])
    {
        int e=v[i];
        if(e!=fa[k]&&e!=son[k])
        {
            dfs2(e,e);
            out[k]=max(out[k],out[e]);
        }
    }
}

int query(int s,int k)
{
    s=fa[s];
    int f1=top[s];
    while(k)
    {
        int d=in[s]-in[f1]+1;
        if(k>d)
        {
            k-=d;
            s=fa[f1];
            f1=top[s];
        }
        else
        {
            s=inv[ in[s]-k+1 ];
            break;
        }
    }
    return s;
}

int main()
{
    init_();
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        if(a[i]) add_edge(a[i],i),add_edge(i,a[i]);
    }
    for(int i=1;i<=n;i++)
    {
        if(!a[i])
        {
            dfs1(i,-1,0);
            dfs2(i,i);
        }
    }
    for(int i=1;i<=n;i++)
    {
        sort(vec[i].begin(),vec[i].end());
    }
    int q;
    scanf("%d",&q);
    while(q--)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        if(deep[a]<b||b==0)
        {
            printf("0 ");
            continue;
        }
        int lca=query(a,b);

        int d=deep[a];
        int l=lower_bound(vec[d].begin(),vec[d].end(),in[lca])-vec[d].begin();
        int r=upper_bound(vec[d].begin(),vec[d].end(),out[lca])-vec[d].begin();
        printf("%d ",r-l-1);
    }
}
/*
9
0 1  2 3 4 4 3 2 1
*/
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值