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

題目鏈接: http://codeforces.com/problemset/problem/246/E

題意:

你現在有一棵 1e51e5 個結點的樹,每個結點有一個權值。你現在有 1e51e5 個詢問,每個詢問會有兩個值 v,kv,k ,詢問的是結點 vv 的往下第 kk 代所有結點中有多少個不同的權值。

做法:

因爲每一層的結點都已經固定了,所以我們可以按照層序遍歷來給每一層的結點一個位置。這樣的話詢問中的——結點 vv 的往下第 kk 代所有結點,就會變成一個區間,等於是我們在這個區間中找有多少個不同的值。這就是一個主席樹的板子問題。

但是怎麼去找這個區間呢,這就用到了二分,兩次二分找一個左邊界和一個右邊界即可。

代碼

#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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章