建树与树的启发式合并

CF C. Tree Cutting
建树的一道比较经典题目。

点击查看代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<map>
#include<cstdlib>
#include<set>
#include<vector>
#include<iostream>
using namespace std;
#define endl '\n'
const int N=2e5+10;
const int mod=1e9+7;
#define ll long long 
#define ull unsigned long long
int num[N];
vector<int> vis[N];
int siz[N],ret;
void dfs(int now,int fa,int x) {
    siz[now]=1;
    for(auto vi : vis[now]) {
        if(vi==fa)
            continue;
        dfs(vi,now,x);
        if(siz[vi]>=x) {
            ret++;
            continue;
        }
        siz[now]+=siz[vi];//不被计算喽
    }
}
bool check(int x,int k) {
    ret=0;
    dfs(1,-1,x);
    if(siz[1]<x)
        return (ret-1)>=k;
    else 
        return ret>=k;
}
void solve() {
    int n,k;
    cin>>n>>k;
    int i;
    int u,v;
    for(i=1;i<=n;i++) {
        num[i]=0;
        // siz[i]=0;
        vis[i].clear();
    }
    for(i=1;i<n;i++) {
        cin>>v>>u;
        vis[v].push_back(u);
        vis[u].push_back(v);
    }
    int le=1,ri=n;
    int mid;
    while(le+1<ri) {
        mid=(le+ri)>>1;
        if(check(mid,k)) {
            le=mid;
        }else {
            ri=mid-1;
        }
    }
    if(check(ri,k)) {
        printf("%d\n",ri);
    }else {
        printf("%d\n",le);
    }
    return;
}
int main() {
    int t;
    cin>>t;
    while(t--)
        solve();
    return 0;
}

树上启发式合并。
P9233 [蓝桥杯 2023 省 A] 颜色平衡树
很板的一道题。主要是轻重子树的区别对待和颜色桶还有这个数量有多少个颜色的一个桶的应用。

点击查看代码
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<map>
#include<cstdlib>
#include<set>
#include<vector>
#include<iostream>
using namespace std;
#define endl '\n'
const int N=2e5+10;
const int mod=1e9+7;
#define ll long long 
#define ull unsigned long long
int c[N],f[N],son[N]={0},siz[N]={0};
int cnt[N]={0},ccnt[N]={0};
int ans=0;;
vector<int> a[N];
void dfs(int u) {//寻找重儿子
    siz[u]=1;
    for(auto v:a[u]) {
        dfs(v);
        siz[u]+=siz[v];
        if(siz[v]>siz[son[u]]) {
            son[u]=v;
        }
    }
    return;
}
void add(int u,int dt) {//痕迹
    --ccnt[cnt[c[u]]];
    cnt[c[u]]+=dt;
    ++ccnt[cnt[c[u]]];
    for(auto v:a[u])
        add(v,dt);
    return;
}
void cal(int u,bool save) {
    for(auto v:a[u]) {//查找轻儿子
        if(v!=son[u])
            cal(v,false);
    }
    if(son[u])
        cal(son[u],true);//查找重儿子
    --ccnt[cnt[c[u]]];//该节点要加入计算,那么数目为cnt[c[u]]的数目少一个
    ++cnt[c[u]];;//颜色为c[u]的节点增加了
    ++ccnt[cnt[c[u]]];
    for(auto v:a[u]) {//加入计算轻儿子的影响
        if(v!=son[u])
            add(v,1);
    }
    // printf("]%d %d\n",u,ans);
    if(cnt[c[u]]*ccnt[cnt[c[u]]]==siz[u])
        ++ans;
    // printf("[%d %d\n",u,ans);
    if(!save)//轻儿子的悲惨命运,被擦除
        add(u,-1);
}
void solve() {
    int n;
    cin>>n;
    int i;
    for(i=1;i<=n;i++) {
        cin>>c[i]>>f[i];
        if(f[i]) {
            a[f[i]].push_back(i);
        }
    }
    dfs(1);
    cal(1,true);
    cout<<ans<<endl;
    return;
}
int main() {
    int t=1;
    // cin>>t;
    while(t--)
        solve();
    return 0;
}
posted @ 2024-04-16 18:10  WE-R  阅读(10)  评论(0)    收藏  举报