专栏文章

题解:P13918 [PO Final 2024] 雪崩 / Avalanche

P13918题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@minhfcq8
此快照首次捕获于
2025/12/02 02:28
3 个月前
此快照最后确认于
2025/12/02 02:28
3 个月前
查看原文
因为要最大值最小,所以一眼二分答案。
假设我们现在的二分中点是 midmid。对整棵树进行类似树形 DP 的方法。我们设以点 ii 为根的子树大小为 sizisiz_i。显然,如果 sizi>midsiz_i>mid 了,我们就必须建造一堵墙,计数器加一。如果计数器大于 kk,就说明不合法,往右边查找,否则就往左边查找。时间复杂度 O((n+m)logn)O((n+m) \log n)
AC 代码:
CPP
#include <iostream>
#include <vector>
#define int long long
#define endl '\n'
using namespace std;

const int N = 1e5+5;
vector<int> g[N];
int n,k;
int siz[N];
int dfs(int now,int fa,int mid){
    siz[now] = 1;
    int sum = siz[now];//存子树大小
    int sum_ans = 0;//存储子树的答案之和
    for(auto it:g[now]){
        if(it==fa) continue;
        sum_ans+=dfs(it,now,mid);
        sum+=siz[it];
        siz[now]+=siz[it];
    }
    if(sum>mid){//如果超过了 mid,显然要建一堵墙
        siz[now] = 0;
        // cout<<now<<endl;
        return sum_ans+1;//返回答案+1
    }
    return sum_ans;//否则返回答案
}
bool check(int mid){
    int now = dfs(1,-114514,mid);
    // cout<<now<<' ';
    return now<=k;
}
signed main()
{
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    cin>>n>>k;
    for(int i = 2;i<=n;i++){
        int x;
        cin>>x;
        g[x].push_back(i);
        g[i].push_back(x);
    }
    int l = 0,r = n;
    int ans = 0;
    while(l<=r){
        int mid = (l+r)>>1;
        // cout<<"Checking "<<mid<<": ";
        if(check(mid)){
            ans = mid;
            r = mid-1;
            // cout<<"Success"<<endl;
        }else l = mid+1/*,cout<<"Unsuccess"<<endl*/;
    }
    cout<<ans;
    return 0;
}

评论

0 条评论,欢迎与作者交流。

正在加载评论...