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;
}

猜你喜欢

转载自www.cnblogs.com/LLTYYC/p/10946141.html