cf 246 E. Blood Cousins Return 二分+主席树

题目链接: http://codeforces.com/problemset/problem/246/E

题意:

你现在有一棵 1 e 5 1e5 1e5 个结点的树,每个结点有一个权值。你现在有 1 e 5 1e5 1e5 个询问,每个询问会有两个值 v , k v,k v,k ,询问的是结点 v v v 的往下第 k k k 代所有结点中有多少个不同的权值。

做法:

因为每一层的结点都已经固定了,所以我们可以按照层序遍历来给每一层的结点一个位置。这样的话询问中的——结点 v v v 的往下第 k k k 代所有结点,就会变成一个区间,等于是我们在这个区间中找有多少个不同的值。这就是一个主席树的板子问题。

但是怎么去找这个区间呢,这就用到了二分,两次二分找一个左边界和一个右边界即可。

代码

#include <bits/stdc++.h>
#define rep(i,a,b) for(int i = (int)a;i<=(int)b;i++)
#define rep_e(i,u,v) for(int i=head[u],v=to[i];~i;i=nex[i],v=to[i])
using namespace std;
typedef long long ll;
const int maxn=100005;
const int maxm=200005;
const int inf=1e9+7;
int rt[maxn*40],ls[maxn*40],rs[maxn*40],sz[maxn*40];
int a[maxn],n,q,ct,fa[maxn][25],dep[maxn],L[maxn],R[maxn];
int id[maxn],rid[maxn],ctmp,pos[maxn],d[maxn],val[maxn];

int head[maxn],to[maxm],nex[maxm],cnt;
void add(int u,int v){
    to[cnt]=v;nex[cnt]=head[u];
    head[u]=cnt++;
}
unordered_map<string,int > mp;
//id-i's pos£¬rid - posΪiµÄÊÇÄĸöid
void update(int &now,int las,int l,int r,int pos,int v){
    now=++ct;
    ls[now]=ls[las];
    rs[now]=rs[las];
    sz[now]=sz[las]+v;
    if(l==r) return ;
    int mid=(l+r)/2;
    if(pos<=mid) update(ls[now],ls[las],l,mid,pos,v);
    else update(rs[now],rs[las],mid+1,r,pos,v);
}
int query(int now,int las,int l,int r,int ql,int qr){
    if(ql<=l&&r<=qr){
        return sz[now]-sz[las];
    }
    int mid=(l+r)/2,ret=0;
    if(ql<=mid) ret+=query(ls[now],ls[las],l,mid,ql,qr);
    if(qr>mid) ret+=query(rs[now],rs[las],mid+1,r,ql,qr);
    return ret;
}
int gain(string s){
    if(!mp[s]) mp[s]=++ctmp;
    return mp[s];
}
void bfs(){
    queue<int> Q;
    Q.push(1);
    int tmp=0;
    while(!Q.empty()){
        int v=Q.front(); Q.pop();
        id[v]=++tmp;
        a[tmp]=val[v];
        rid[tmp]=v;
        rep_e(i,v,x){
            Q.push(x);
        }
    }
}
void dfs(int u,int dep){
    d[u]=dep;
    L[dep]=min(L[dep],id[u]);
    R[dep]=max(R[dep],id[u]);
    for(int i=head[u];~i;i=nex[i]){
        fa[to[i]][0]=u;
        dfs(to[i],dep+1);
    }
}

void build(){
    for(int j=1;(1<<j)<=n;j++)
        for(int i=1;i<=n;i++)
            fa[i][j]=fa[fa[i][j-1]][j-1];
    for(int i=1;i<=n;i++){
        update(rt[i],rt[i-1],1,n,i,1);
        if(pos[a[i]]){
            int tmp=rt[i];
            update(rt[i],tmp,1,n,pos[a[i]],-1);
        }
        pos[a[i]]=i;
    }
}
int getfa(int u,int k){
    for(int i=21;i>=0;i--){
        if((k&(1<<i))==(1<<i)){
            u=fa[u][i];
        }
    }
    return u;
}
int main(){
    memset(head,-1,sizeof(head));
    scanf("%d",&n);
    rep(i,2,n+1){
        string s; int ff;
        cin>>s>>ff;
        int ID=gain(s);
        val[i]=ID; ff++;
        add(ff,i);
    }
    n++;
    bfs();
    rep(i,1,n) L[i]=inf,R[i]=0;
    dfs(1,1);
    build();
    scanf("%d",&q);
    while(q--){
        int p,k; scanf("%d%d",&p,&k); p++;
        int aimd=d[p]+k,l,r,aiml=-1,aimr=-1;
        l=L[aimd],r=R[aimd];
        while(l<=r){
            int mid=(l+r)/2;
            int faa=getfa(rid[mid],k);
            if(id[faa]>=id[p]) aiml=mid,r=mid-1;
            else l=mid+1;
        }
        l=L[aimd],r=R[aimd];
        while(l<=r){
            int mid=(l+r)/2;
            int faa=getfa(rid[mid],k);
            if(id[faa]<=id[p]) aimr=mid,l=mid+1;
            else r=mid-1;
        }
        if(getfa(rid[aiml],k)!=p||aiml<L[aimd]||aiml>R[aimd]||aimr<L[aimd]||aimr>R[aimd]) {
            printf("0\n");
            continue;
        }
        printf("%d\n",query(rt[aimr],0,1,n,aiml,aimr));
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值