题目

描述

\(n\) 个点的树,每条边有一个边权;

对于一个 \(X\) ,求删去一些边后使得每个点的度数 \(d_i\) 均不超过 \(X\) 的最小代价;

你需要依次输出 \(X=0 \to n-1\) 的答案;

范围

$ 1 \le n \le 250000 $

题解

  • 考虑对于一个\(X\)怎么做,设 $ dp_{i,0/1} $ 表示 $ u $ 节点的子树,$ i $连向父亲的边是否被删且度数不超过 $ X $ 的最小代价。转移时将 \(dp_{v,1} + w(u,v) - dp_{v,0}\) 排序,小于 \(0\) 的优先选,再补到 $ d_{u} - X (-1) $ 个即可 。

    复杂度 : \(O(n^2)\)

  • 注意到 \(\sum_{i=0}^{n-1}\sum_{j=1}^{n} [d_j>i] = \sum_{i=1}^{n} d_{j} = 2n-2 = O(n)\) 。

    升序考虑\(X\),当一个点的度数小于等于\(X\)的时候可以直接删去这个点并把这个点的贡献加入相邻的点中;

    可以用一个支持删除的堆或者set实现。再执行同样的\(dp\);

  • 但是直接维护所有点仍是\(O(n^2log \ n )\)的,需要把复杂度降到只和当前保留的点有关。

    注意到对于一个 $ u $ , $ d_{u}-X(-1) $ 在减小,维护堆中所有要选的$ d_{u}-X( -1 ) $个点的 $ sum $ ,这样子每次转移就只需要个新加儿子的\(dp\)值,然后调整堆的\(size\)即可,转移完后最后再还原;

    这样子转移复杂度就只和度数​\(>=X\)的点有关;

    复杂度:\(O(n \log n)\) ;

    #include<bits/stdc++.h>
    #define pb push_back
    #define fi first
    #define se second
    #define mk make_pair
    #define ll long long
    using namespace std;
    const int N=250010;
    int n,D,vis[N],d[N],nxt[N],st[N];
    ll sum[N],ans,f[N][2];
    typedef pair<int,int>pii;
    vector<pii>g[N];
    vector<int>vec[N];
    int cnt;
    bool cmp(const pii&a,const pii&b){
    return d[a.fi]<d[b.fi];
    }
    struct data{
    priority_queue<ll>A,B;
    void push(ll x){
    // cnt++;
    A.push(x);
    }
    void del(ll x){
    // cnt++;
    B.push(x);
    }
    int top(){
    while(!B.empty()&&A.top()==B.top())A.pop(),B.pop();
    return A.top();
    }
    void pop(){
    top();A.pop();
    }
    int size(){
    return A.size()-B.size();
    }
    bool empty(){
    return A.size()==B.size();
    }
    }q[N];
    void update(int u){
    vis[u]=1;
    for(int i=0;i<g[u].size();++i){
    int v=g[u][i].fi,w=g[u][i].se;
    if(vis[v])continue;
    q[v].push(w),sum[v]+=w;
    }
    }
    void resize(int u,int num){
    while(q[u].size()>num){
    sum[u]-=q[u].top();
    q[u].pop();
    }
    }
    void resize(int u,int num,vector<ll>&add){
    while(q[u].size()>num){
    sum[u]-=q[u].top();
    add.pb(q[u].top());
    q[u].pop();
    }
    }
    void dfs(int u,int F){
    /*{
    cnt++;
    }*/
    vis[u]=1;
    int num=d[u]-D;
    resize(u,num);
    vector<ll>add,del;
    ll all=0;
    while(st[u]<g[u].size()&&d[g[u][st[u]].fi]<=D)st[u]++;
    for(int i=st[u];i<g[u].size();++i){
    int v=g[u][i].fi,w=g[u][i].se;
    if(vis[v]||v==F)continue;
    dfs(v,u);
    if(f[v][1]+w<=f[v][0])num--,all+=f[v][1]+w;
    else {
    all+=f[v][0];
    ll tmp=f[v][1]+w-f[v][0];
    q[u].push(tmp),sum[u]+=tmp;
    del.pb(tmp);
    }
    }
    resize(u,max(0,num),add);
    f[u][0]=all+sum[u];
    resize(u,max(0,--num),add);
    f[u][1]=all+sum[u];
    for(auto x : add)q[u].push(x),sum[u]+=x;
    for(auto x : del)q[u].del(x),sum[u]-=x;
    }
    int main(){
    //freopen("F.in","r",stdin);
    //freopen("F.out","w",stdout);
    scanf("%d",&n);
    for(int i=1,u,v,w;i<n;++i){
    scanf("%d%d%d",&u,&v,&w);
    g[u].pb(mk(v,w));
    g[v].pb(mk(u,w));
    d[u]++,d[v]++;
    ans+=w;
    }
    for(int i=1;i<=n;++i){
    vec[d[i]].pb(i);
    sort(g[i].begin(),g[i].end(),cmp);
    }
    nxt[n]=n+1;
    for(int i=n-1;i;--i){
    if(vec[i+1].size())nxt[i]=i+1;
    else nxt[i]=nxt[i+1];
    }
    printf("%I64d ",ans);
    for(int i=1;i<n;++i){
    for(auto j : vec[i])update(j);
    ans=0;D=i;
    for(int j=i+1;j<=n;j=nxt[j])
    for(auto k : vec[j])if(!vis[k]){
    dfs(k,0);ans+=f[k][0];
    }
    for(int j=i+1;j<=n;j=nxt[j])
    for(auto k : vec[j]){
    vis[k]=0;
    }
    printf("%I64d ",ans);
    }
    //cerr<<fixed<<setprecision(10)<<1.0*clock()/CLOCKS_PER_SEC<<endl;
    return 0;
    }

【codeforces contest 1119 F】Niyaz and Small Degrees的更多相关文章

  1. [codeforces contest 1119 F] Niyaz and Small Degrees 解题报告 (树形DP+堆)

    interlinkage: http://codeforces.com/contest/1119/problem/F description: 有一颗$n$个节点的树,每条边有一个边权 对于一个$x$ ...

  2. 【cf contest 1119 H】Triple

    题目 给出 \(n\) 个三元组\(\{ a_i,b_i,c_i \}\)和\(x,y,z\): 将每个三元组扩展成(\(x\)个\(a_i\),\(y\)个\(b_i\),\(z\)个\(c_i\) ...

  3. 【cf contest 1119 G】Get Ready for the Battle

    题目 你有\(n\)个士兵,需要将他们分成\(m\)组,每组可以为0: 现在这些士兵要去攻打\(m\)个敌人,每个敌人的生命值为\(hp_i\) : 一轮游戏中一组士兵选定一个攻打的敌人,敌人生命值- ...

  4. 【Codeforces #312 div2 A】Lala Land and Apple Trees

    # [Codeforces #312 div2 A]Lala Land and Apple Trees 首先,此题的大意是在一条坐标轴上,有\(n\)个点,每个点的权值为\(a_{i}\),第一次从原 ...

  5. 【Educational Codeforces Round 37 F】SUM and REPLACE

    [链接] 我是链接,点我呀:) [题意] 在这里输入题意 [题解] 那个D函数它的下降速度是很快的. 也就是说到最后他会很快的变成2或者1 而D(2)==2,D(1)=1 也就是说,几次操作过后很多数 ...

  6. 【AtCoder Regular Contest 082 F】Sandglass

    [链接]点击打开链接 [题意] 你有一个沙漏. 沙漏里面总共有X单位的沙子. 沙漏分A,B上下两个部分. 沙漏从上半部分漏沙子到下半部分. 每个时间单位漏1单位的沙子. 一开始A部分在上面.然后在r1 ...

  7. 【Atcoder Grand Contest 011 F】Train Service Planning

    题意:给\(n+1\)个站\(0,\dots,n\),连续的两站\(i-1\)和\(i\)之间有一个距离\(A_i\),其是单行(\(B_i=1\))或双行(\(B_i=2\)),单行线不能同时有两辆 ...

  8. 【AtCoder Regular Contest 076 F】Exhausted (贪心)

    Description 机房里有M台电脑排成一排,第i台电脑的坐标是正整数i. 现在有N个OIer进入了机房,每个OIer需要一台电脑来学tui习ji,同时每个OIer对自己电脑所处的坐标范围有一个要 ...

  9. 【郑轻邀请赛 F】 Tmk吃汤饭

    [题目链接]:https://acm.zzuli.edu.cn/zzuliacm/problem.php?id=2132 [题意] [题解] 很容易想到用队列来模拟; 这个队列维护的是正在煮的4个人煮 ...

随机推荐

  1. libmysqlclient.so.16: cannot open shared object file: No such file or directory

    编译安装的mysql5.6.39,安装目录是/usr/local/mysql,启用程序时报错:libmysqlclient.so.16: cannot open shared object file: ...

  2. Coolest Ski Route-不定起点和终点----在有向变的情况下---求最长路

    这题最开始给你了N个点,M条边,边是单向边,问不指定起点和终点,最长路是什么??? 脑补一下,不定起点和终点的最短路,用弗洛伊德算法搞一搞,但是...那个垃圾算法的复杂度是N^3的,但是这个算法的M高 ...

  3. 词频统计 List Array

    c# 使用数组进行词频统计 1.先考虑要是使用的数据结构: Array在在内存中是连续存储的,所以它的索引速度非常快,而且赋值与修改元素也很简单,但是数组存在一些不足的地方.在数组的两个数据间插入数据 ...

  4. vue js 在组件中对数组使用splice() 遇到的坑。。。

    遇到的问题: 用el-dialog写了个子组件 要实现在子组件中增删数据 点击确定后把值返回给父组件 父组件在每次点开子组件时都会把自己的值传进去. //父组件传值 this.$refs.transf ...

  5. PHP常用类------生成验证码类Code

    直接附上代码吧!很简单的代码,写一遍基本就会了,主要明白用GD库画图的几个步骤: 1.先创建画布,不然画在哪儿?(imagecreatetruecolor)2.根据要画的数据,选定颜色 (imagec ...

  6. Ajax的注意事项

    case 1: 无论是使用原生的JavaScript,还是JQuery,通过Ajax请求后端程序数据,返回的数据默认是字符串,字符串,字符串,重要的事情说三遍!!! case 2: 不要尝试直接将返回 ...

  7. Centos7安装Splash

    前言 最近在用Scrapy抓取一个网站数据,其中有个页面需要执行js脚本才能加载,所以需要用到Splash. 官网地址:https://splash.readthedocs.io/en/stable/ ...

  8. PostgreSQL字段类型说明

    BIGSERIALSERIAL8 存储自动递增的惟一整数,最多 8 字节. BIT 固定长度的位串. BIT VARYING(n)VARBIT(n) 可变长度的位串,长度为 n 位. BOOLEAN  ...

  9. C# 语言习惯

    目录 一.使用属性而不是可访问的数据成员 二.使用运行时常量(readonly)而不是编译时常量(const) 三.推荐使用 is 或 as 操作符而不是强制类型转换 四.使用 Conditional ...

  10. python 抽象类与接口类

    几个类 实现的方法都一致的话 就继承同一个父类 在父类写一个公共方法 给子类使用