CodeForces 246 E.Blood Cousins Return (dsu on tree+set去重)

题意:

给n个点的树,每个点有一个名字,不同的点名字可能相同
m次查询,给出x k,求以x为根的子树,深度为d[x]+k的点中有多少种不同的名字

思路:

和cf 208e差不多
离线储存询问,问题就变成离线子树问题,用dsu on tree解决,

询问储存的方式是:二元组存储问题id和x的层数,
每当统计完根为fa的子树之后,遍历以fa为根的询问,然后更新答案,具体看代码

cal函数计算的是以当前点为根的子树中,各个深度的名字数,
去重用set就行了,添加和删除分别对应set的insert和erase

code:

#include<bits/stdc++.h>
using namespace std;
const int maxm=1e5+5;
vector<int>g[maxm];
vector<pair<int,int> >q[maxm];
int sz[maxm],son[maxm],d[maxm];
int mark[maxm];
map<string,int>name;
int id[maxm],idx;//记录编号为i的人的名字id
set<int>cnt[maxm];//cnt[i]表示深度为i的人名数
int res[maxm];
int n,m;
void dfs(int x,int fa){
    //树形dp求轻重儿子
    sz[x]=1;
    son[x]=-1;
    for(int v:g[x]){
        if(v==fa)continue;
        d[v]=d[x]+1;
        dfs(v,x);
        sz[x]+=sz[v];
        if(son[x]==-1||sz[son[x]]<sz[v]){
            son[x]=v;
        }
    }
}
void cal(int x,int fa,int change){
    if(change>0){
        cnt[d[x]].insert(id[x]);
    }else{
        cnt[d[x]].erase(id[x]);
    }
    for(int v:g[x]){
        if(v==fa||mark[v])continue;
        cal(v,x,change);
    }
}
void solve(int x,int fa,int kep){
    for(int v:g[x]){//先解决轻儿子
        if(v==fa||v==son[x])continue;
        solve(v,x,0);
    }
    if(son[x]!=-1){//再解决重儿子
        solve(son[x],x,1);
        mark[son[x]]=1;
    }
    cal(x,fa,1);//计算轻儿子
    for(auto i:q[x]){//遍历以x为祖先的询问,更新询问的答案
        res[i.first]=cnt[i.second].size();
    }
    if(son[x]!=-1){
        mark[son[x]]=0;
    }
    if(!kep){
        cal(x,fa,-1);
    }
}
signed main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        char s[30];
        int x;
        scanf("%s%d",s,&x);
        if(!name[s])name[s]=++idx;
        id[i]=name[s];
        g[i].push_back(x);
        g[x].push_back(i);
    }
    dfs(0,0);
    scanf("%d",&m);
    for(int i=1;i<=m;i++){
        int x,k;
        scanf("%d%d",&x,&k);
        if(d[x]+k>=maxm){//如果深度越界
            res[i]=0;
            continue;
        }
        q[x].push_back(make_pair(i,d[x]+k));//询问id和深度d[x]的二元组询问存入q[fa]中
    }
    solve(0,0,1);
    for(int i=1;i<=m;i++){
        printf("%d ",res[i]);
    }
    puts("");
    return 0;
}
发布了364 篇原创文章 · 获赞 26 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/weixin_44178736/article/details/103171716
今日推荐