zoukankan      html  css  js  c++  java
  • Codeforces 208E. Blood Cousins

    传送门

    题目大意:

    小C喜欢研究族谱,这一天小C拿到了一整张族谱。

    小C先要定义一下k-祖先。

    • x的1-祖先指的是x的父亲
    • x的k-祖先指的是x的(k-1)-祖先的父亲

    小C接下来要定义k-兄弟

    • x的k-兄弟指的是与x的k-祖先相同的人
    • 如果不存在k-祖先那么x没有k-兄弟

    小C想问问你,x到底有多少k-兄弟?小C打算问Q次这样的问题。

    数据范围:

    $n<=10^5,Q<=10^5$

    $dsu on tree$ 基础题,当然也有显然的在线做法

    对于每个询问 $(x,k)$,不妨转换为 $(u,v)$ ,表示求 $u$ 的子树中,与 $v$ 深度相同的节点数

    考虑怎么离线搞,直接 $dsu on tree$ 维护一个统计各个深度节点数的数组 $cnt$ 即可

    每次 $dfs$ 时最后 $dfs$ 重儿子,从而保留重儿子的 $cnt$ ,这样与每个节点 $u$ 有关的询问只要暴力枚举轻儿子子树即可

    复杂度就是启发式合并的 $nlog_n$

    具体看代码,注意数据是森林

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cmath>
    #include<cstring>
    #include<set>
    using namespace std;
    typedef long long ll;
    inline int read()
    {
        int x=0,f=1; char ch=getchar();
        while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
        while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
        return x*f;
    }
    const int N=2e6+7;
    int fir[N],from[N<<1],to[N<<1],cntt;
    inline void add(int a,int b) { from[++cntt]=fir[a]; fir[a]=cntt; to[cntt]=b; }
    int f[N][21],dep[N],son[N],sz[N];//f是倍增数组,dep是节点深度,son是重儿子,sz是子树大小
    void dfs1(int x)//预处理上面四个数组
    {
        dep[x]=dep[f[x][0]]+1; sz[x]=1; int mx=0;
        for(int i=1;i<=20;i++) f[x][i]=f[f[x][i-1]][i-1];
        for(int i=fir[x];i;i=from[i])
        {
            int &v=to[i]; if(v==f[x][0]) continue;
            f[v][0]=x; dfs1(v);
            if(sz[v]>sz[son[x]]) son[x]=v;
            sz[x]+=sz[v];
        }
    }
    struct dat{
        int x,y,id;
        inline bool operator < (const dat &tmp) const {
            return x<tmp.x;
        }
    }d[N];//存询问
    int n,Q;
    int cnt[N],ans[N],id[N],dfs_clock;//id是dfs序为i的节点编号
    void dfs2(int x,bool flag)//flag判断是否保留cnt
    {
        id[++dfs_clock]=x; int L=dfs_clock;//轻儿子子树dfs序的左区间
        if(!son[x]) { if(flag) cnt[dep[x]]++; return; }
        for(int i=fir[x];i;i=from[i])
        {
            int &v=to[i]; if(v==f[x][0]||v==son[x]) continue;
            dfs2(v,0);//走轻儿子
        }
        int R=dfs_clock,t=lower_bound(d+1,d+Q+1,(dat){x,0,0})-d;//R是轻儿子子树右区间,t是第一个u为x的询问的位置
        dfs2(son[x],1);//最后走重儿子并保留cnt
        for(int i=L;i<=R;i++) cnt[ dep[id[i]] ] ++;//枚举轻儿子子树,更新cnt
        for(int i=t;d[i].x==x;i++) { ans[d[i].id]=cnt[dep[d[i].y]]-1; }//更新ans
        if(!flag) for(int i=L;i<=dfs_clock;i++) cnt[ dep[id[i]] ]--;//清空cnt
    }
    int rt[N],tot;//注意是森林,要存每个数的根
    int main()
    {
        n=read(); int a,b;
        for(int i=1;i<=n;i++)
        {
            a=read();
            add(a,i);
            if(!a) rt[++tot]=i;
        }
        for(int i=1;i<=tot;i++) dfs1(rt[i]);
        Q=read();
        for(int i=1;i<=Q;i++)
        {
            a=read(),b=read(); int t=a;
            for(int j=20;j>=0;j--) if(b&(1<<j)) t=f[t][j];
            d[i].x=t,d[i].y=a,d[i].id=i;
        }
        sort(d+1,d+Q+1);
        for(int i=1;i<=tot;i++) dfs2(rt[i],0);
        for(int i=1;i<=Q;i++) printf("%d ",ans[i]);
        return 0;
    }
  • 相关阅读:
    importlib
    js给kindeditor添加值
    在kindeditor 获取textarea 中 输入的值
    获取lable选中时触发事件
    Django之ModelForm组件
    KindEditor 和 xss过滤
    from 动态显示select数据
    CBV 验证装饰器的使用
    views获取数据 -- request包含的方法
    django -- 自定义simpletag 和 filter
  • 原文地址:https://www.cnblogs.com/LLTYYC/p/10946141.html
Copyright © 2011-2022 走看看