[https://codeforces.com/problemset/problem/246/E] (点此看题)

简要题面

一棵树上有n个节点,每个节点有对应的名字(名字可重复)。

每次询问,求深度比$vi$多$ki$的$vi$的儿子中,有多少种名字

分析: 

Step1:

我们可以懂$DFS$轻松找到每个节点的深度dep[x], 同时用$DFS$序列得知每个节点间的关系(也就是说,可以用in[x]与ou[x],来知道另一个节点$v$是不是$x$的儿子)。

做完以上工作,本题所求的结果即是 已知深度dep,求in[x]在in[father]~ou[father]中的$x$,他们名字共有多少种

Stepp:

要求一段区间中不同的种类,我们可以联想到**HH的项链**[https://www.luogu.com.cn/problem/P1972] (点此进入)

我们可以用很多方法来完成这道题,像**莫队、主席树、树状数组等** 这道题我用的是树状数组 先贴一个HH的项链的code吧

vjudge :: Blood Cousins Return_树状数组vjudge :: Blood Cousins Return_ios_02
#include<bits/stdc++.h>
using namespace std;
#define re register int
const int N=1e6+5;
int n, m, a[N], c[N];
inline int lowbit(const int x) {return x&-x;}
inline int getsum(const int x)
{
    int ret=0;
    for(re i=x;i > 0;i-=lowbit(i)) ret += c[i];
    return ret;
}
inline void modi(const int x,const int d)
{
    for(re i=x;i<=1000000;i+=lowbit(i)) c[i] += d;
}
struct node{
    int a, b, id;
    bool operator<(const node&x)const{
        return x.b > b; // 右端点从小到大排序 
    }
}ask[N];
int lst[N], ans[N]; // lst[i]表示 颜色i最近一次出现的位置 
signed main()
{
    scanf("%d",&n);
    for(re i=1;i<=n;++i) scanf("%d",&a[i]);
    scanf("%d",&m);
    for(re i=1;i<=m;++i)
    {
        scanf("%d%d",&ask[i].a,&ask[i].b);
        ask[i].id = i;
    }
    sort(ask+1, ask+1+m); // 将询问区间 由右端点从小到大 排序 
    for(re i=1, k=1;i<=n && k<=m;++i)
    {
        modi(i, 1); // 每次到一个新的点,将区间[i,n] +1 
        if(lst[a[i]]) modi(lst[a[i]], -1); 
        // 如果之前已经有过这个颜色 ,将曾经的颜色位置 -1
        // 正确性:我们已经将询问排了序,所以以后的询问只需要离右端点近的颜色 
        lst[a[i]] = i;
        while(k <= m && ask[k].b == i)
        {
            ans[ask[k].id] = getsum(i)- getsum(ask[k].a-1); // 注意减一 
            k++;
        }
    }
    for(re i=1;i<=m;++i) printf("%d\n", ans[i]);
}
View Code

 

 该题代码

#include<bits/stdc++.h>
using namespace std;
#define re register int
const int N=1e6+5, M=4e6;
map<string, int>mp;
vector<int>V[N];
int n, total, in[N], ou[N], dep[N];
int tt, las[N], nt[M], ed[M], bel[N], maxdep;
inline void add(const int x, const int y)
{
    ed[++tt]=y; nt[tt]=las[x]; las[x]=tt;
}
void DFS(const int x,const int FA)
{
    in[x] = ++total;
    bel[total] = x;
    dep[x] = dep[FA] + 1;
    maxdep = max(maxdep,dep[x]);
    
    if(V[dep[x]].size() == 0) V[dep[x]].push_back(-1);
    V[dep[x]].push_back(in[x]);
    
    for(re i=las[x];i;i=nt[i]) DFS(ed[i], x);
    ou[x] = total;
}
struct node{int a, b, id;}ask[N];
vector<int>TO[N];
bool cmp(const int &x, const int &y)
{
    return ask[x].b < ask[y].b;
}
int a[N], c[N], lst[N], ans[N];
inline int lowbit(const int x) {return x&-x;}
inline int getsum(const int x)
{
    int ret=0;
    for(re i=x;i > 0;i-=lowbit(i)) ret += c[i];
    return ret;
}
inline void modi(const int x,const int d)
{
    for(re i=x;i<=n;i+=lowbit(i)) c[i] += d;
}
inline void solve(const int de) // 单独处理每一深度de 
{
    memset(lst, 0, sizeof(lst));
    memset(c, 0 ,sizeof(c));
    
    sort(TO[de].begin()+1, TO[de].end(), cmp);
    int nn = V[de].size()-1, mm=TO[de].size()-1;
    for(re i=1,k=1; i<=nn && k<=mm; ++i)
    {
        modi(i, 1); // 树状数组 
        int rea = bel[V[de][i]];
        int color = a[rea];
        if(lst[color] != 0) modi(lst[color], -1);
        lst[color] = i;
        
        while(k<=mm && ask[TO[de][k]].b == i)
        {
            int tp = TO[de][k];
            ans[ask[tp].id] = getsum(i) - getsum(ask[tp].a-1);
            k++;
        }
    }
}
signed main()
{
    ios::sync_with_stdio(false);
    cin>>n;
    int gf=2e5, m;
    for(re i=1, getname=0;i<=n;++i)
    {
        string name; int father;
        cin>>name>>father;
        if(father != 0) add(father, i);
        else add(gf, i);
        if(!mp.count(name)) mp[name] = ++getname;
        // 记录每种名字所代表的数字 
        a[i] = mp[name];
    }
    dep[0]=-1;
    DFS(gf, 0); // 一个虚拟的根节点,使森林变成一棵树 
    cin>>m;
    for(re i=1;i<=m;++i)
    {
        int x, y;
        cin>>x>>y;
        int dd = dep[x] + y;
        if(dd <= maxdep && V[dd].size())
        {
            int l = lower_bound(V[dd].begin()+1, V[dd].end(), in[x])-V[dd].begin();
            int r = upper_bound(V[dd].begin()+1, V[dd].end(), ou[x])-V[dd].begin()-1; // 记录在dd深度中,从l到r位 
            if(l <= r && 1 <= l && r < V[dd].size())
            {
                ask[i]=(node){l, r, i};
                if(TO[dd].size() == 0) TO[dd].push_back(-1);
                TO[dd].push_back(i);
            }    
        }
    }
    for(re i=1;i<=n;++i) if(TO[i].size()) solve(i);
    for(re i=1;i<=m;++i) cout<<ans[i]<<endl;
}

made by kzsm