CF1119F Niyaz and Small Degrees

题意

有一个 个结点的树,每条边有边权,结点度数就是与之相连的边数量。对于 ,删掉一些边使每个结点的度数不大于 ,求出删掉的边的权值和最小值。

分析

简化问题

求解 时的问题。考虑 ,定义 为,结点 为根子树满足 ,且与父亲的边是否断掉的最小代价。那么对于一个度数大于 的节点,如果不断父亲的边,我们就要选择 个儿子断掉,如果断父亲的边则选择 个儿子。那么贪心的考虑哪些儿子是最优的。我们 的儿子的值放在一个平衡树中,对于小于 的,直接加 。然后贪心选取前几个就好了。时间复杂度为

原问题

考虑由小到大枚举 。如果一个节点的度数已经小于或者等于 ,那么这个节点再也不会在后面的决策中考虑到了。那么只需要在去掉这个节点的森林中每个联通块做一次 。那么每个节点考虑的次数为 ,而 ,对于一个联通块的 一定不会从一个节点转移到一个度数小于等于 的节点,那么可以对边和节点度数排序降低复杂度。所以加上平衡树的复杂度,总的复杂度为

代码

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define P pair<LL,LL>
#define It multiset<LL>::iterator
const int N = 250005;
LL ans,sum[N],f[N][2];
vector<P> G[N<<1]; 
P d[N];
multiset<LL> H[N];
void insert(int x,LL val) {
    H[x].insert(val);
}
void erase(LL x,LL val) {
    It it = H[x].lower_bound(val);H[x].erase(it);
}
bool vis[N];
LL Max,du[N],n;
void die(LL x) {
    for(LL i = 0;i < G[x].size();i++) {
        LL y = G[x][i].second;
        if(du[y] <= Max) break;
        insert(y,G[x][i].first);sum[y] += G[x][i].first;
    }
}
bool cmp(P a,P b){
    return du[a.second] > du[b.second];
}
vector<LL> ins,del;
void dfs(int x,int fa) {
    vis[x] = 1;
    LL m = du[x] - Max;
    while(H[x].size() && H[x].size() > m) {
        It it = H[x].end();--it;sum[x]-= *it;H[x].erase(it);
    } 
    for(LL i = 0;i < G[x].size();i++) {
        LL y = G[x][i].second;
        if(y == fa) continue;
        if(du[y] <= Max) break;
        dfs(y,x); 
    }
    ins.clear();del.clear();
    LL Ans = 0;
    for(int i = 0;i < G[x].size();i++) {
        int y = G[x][i].second;
        if(y == fa) continue;
        if(du[y] <= Max) break;
        LL val = f[y][1] + G[x][i].first - f[y][0];
        if(val <= 0) {Ans += f[y][1] + G[x][i].first;m--;continue;}
        insert(x,val);del.push_back(val);
        Ans += f[y][0];sum[x] += val;
    }
    while(H[x].size() && H[x].size() > m) {
        It it = H[x].end();--it;
        sum[x] -= *it,ins.push_back(*it),H[x].erase(it);
    } 
    f[x][0] = Ans + sum[x];
    while(H[x].size() && H[x].size() > m-1) {
        It it = H[x].end();--it;
        sum[x] -= *it,ins.push_back(*it),H[x].erase(it);
    } 
    f[x][1] = Ans + sum[x];
    for(LL i = 0;i < ins.size();i++) insert(x,ins[i]),sum[x] += ins[i];
    for(LL i = 0;i < del.size();i++) erase(x,del[i]),sum[x] -= del[i];
}
int main() {
    scanf("%lld",&n);    
    for(int i = 1,a,b,c;i < n;i++) {
        scanf("%lld%lld%lld",&a,&b,&c);ans += c;
        G[a].push_back(P(c,b));
        G[b].push_back(P(c,a));
        du[a]++;du[b]++;
    }
    for(int i = 1;i <= n;i++) {
        d[i] = P(du[i],i);
        sort(G[i].begin(),G[i].end(),cmp);
    }
    sort(d+1,d+1+n);
    printf("%lld ",ans);
    LL pos = 1;
    for(Max = 1;Max < n;Max++) {
        while(pos <= n && d[pos].first <= Max) die(d[pos].second),++pos;
        ans = 0;memset(vis,0,sizeof(vis));
        for(int j = pos;j <= n;j++) {
            if(vis[d[j].second]) continue;
            dfs(d[j].second,0);
            ans += f[d[j].second][0];
        }
        printf("%lld ",ans);
    }
    printf("\n");
    return 0;
}

全部评论

相关推荐

勤奋努力的椰子这就开摆:美团骑手在美团工作没毛病
投递美团等公司10个岗位
点赞 评论 收藏
分享
3 收藏 评论
分享
牛客网
牛客企业服务