CF1119F Niyaz and Small Degrees

CF1119F Niyaz and Small Degrees

另类复杂度分析题

对于给定的d

f[x][0/1]x为根的子树,到父亲的边连不连,整个子树的花费最小值

转移时候,设要删掉k的出边,都先变成∑f[son][1],再把f[son][0]+vf[son]-f[son][1]插入堆中,取最小的k个

当f[son][0]+vf[son]-f[son][1]<0一定选择,先选上,令k--

 

所有的d

对于du[x]<=d的x,其度数最后是多少都满足,所以可以不用管了。删除这个点和出边

对剩下的点的连通块进行上述dp

 

很麻烦的事情:

1.du[x]<=d的x删除了,但是其实还要考虑是否会删除

所以把x的所有出边(y,val)的y的堆里面加入val,设这些边为特殊边

2.处理的时候,把会访问儿子的f[son][0]+vf[son]-f[son][1]也加入堆中,

设需要删除k条边

把堆的限制卡成k。然后堆内元素的和就是前k小的值

但是需要还原,所以这个时候删除的东西再用vector存,然后还原,把访问的儿子的f[son][0]+vf[son]-f[son][1]再删除

所以,堆需要懒惰删除+查询总和

堆是大根堆

(或者手写平衡树查前k小?)

3.如果用堆

那么对于特殊边,也要先卡成k,这些扔掉的特殊边就不用了(因为k单调)

这样,每次求dp时候暴力卡成k复杂度就是对的。

否则菊花图就完了。

 

每个点会被访问du[x]次,总复杂度O(nlogn)

#include<bits/stdc++.h>
#define reg register int
#define il inline
#define fi first
#define se second
#define mk(a,b) make_pair(a,b)
#define numb (ch^'0')
#define pb push_back
#define solid const auto &
#define enter cout<<endl
#define pii pair<int,int>
using namespace std;
typedef long long ll;
template<class T>il void rd(T &x){
    char ch;x=0;bool fl=false;
    while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
    for(x=numb;isdigit(ch=getchar());x=x*10+numb);
    (fl==true)&&(x=-x);
}
template<class T>il void output(T x){if(x/10)output(x/10);putchar(x%10+'0');}
template<class T>il void ot(T x){if(x<0) putchar('-'),x=-x;output(x);putchar(' ');}
template<class T>il void prt(T a[],int st,int nd){for(reg i=st;i<=nd;++i) ot(a[i]);putchar('\n');}

namespace Miracle{
const int N=250000+5;
int n;
int du[N],rk[N];
bool cmp1(int x,int y){
    return du[x]<du[y];
}
bool cmp2(int x,int y){
    return du[x]>du[y];
}
struct node{
    int to;ll val;
    node(){}
    node(int x,ll v){
        to=x;val=v;
    }
    bool friend operator <(node a,node b){
        return du[a.to]>du[b.to];
    }
};
vector<node>to[N];
int sta[N],top;
bool vis[N];
ll f[N][2];
struct Heap{
    priority_queue<ll>q,d;
    int sz;
    ll sum;
    void dele(ll v){
        d.push(v);--sz;sum-=v;
    }
    void ins(ll v){
        q.push(v);++sz;sum+=v;
    }
    ll top(){
        while(q.size()&&d.size()&&q.top()==d.top()) q.pop(),d.pop();
        if(q.size()) return q.top();
        return -2333;
    }
    void pop(){
        while(q.size()&&d.size()&&q.top()==d.top()) q.pop(),d.pop();
        if(q.size()){
            sum-=q.top();--sz;
            q.pop();
        }
    }
}hp[N];
int lim;
void sol(int x){
//    cout<<" sol "<<x<<endl;
    int m=max(0,(int)du[x]-lim);
    while(hp[x].sz>m) hp[x].pop();
    vis[x]=1;
    ll tot=0;
    vector<int>a,b;
    for(solid y:to[x]){
        if(vis[y.to]) continue;
        sol(y.to);
        if(f[y.to][0]+y.val-f[y.to][1]<0) --m,tot+=f[y.to][0]+y.val;
        else{
            tot+=f[y.to][1];
            hp[x].ins(f[y.to][0]+y.val-f[y.to][1]);
            b.push_back(f[y.to][0]+y.val-f[y.to][1]);
        }
    }
//    cout<<" m1 "<<m<<" "<<tot<<" sz "<<hp[x].sz<<endl;
    m=max(m,0);
    while(hp[x].sz>m) {
        a.push_back(hp[x].top());
        hp[x].pop();
    }
//    cout<<" end "<<hp[x].sum<<endl;
    f[x][1]=tot+hp[x].sum;
    --m;
    m=max(m,0);
    while(hp[x].sz>m){
        a.push_back(hp[x].top());
        hp[x].pop();
    }
    f[x][0]=tot+hp[x].sum;
    for(solid v:a) hp[x].ins(v);
    for(solid v:b) hp[x].dele(v);
//    cout<<" tot "<<tot<<" m2 "<<m<<endl;
//    cout<<" bac "<<f[x][0]<<" "<<f[x][1]<<endl;
}
int main(){
    rd(n);
    int x,y;ll v;
    for(reg i=1;i<n;++i){
        rd(x);rd(y);rd(v);
        to[x].push_back(node(y,v));
        to[y].push_back(node(x,v));
        ++du[x];++du[y];
    }
    
    for(reg i=1;i<=n;++i) {
        rk[i]=i;
        sort(to[i].begin(),to[i].end());
    }
    sort(rk+1,rk+n+1,cmp1);
    ll ans=0;
    int ptr=1;
    for(lim=0;lim<n;++lim){
        while(ptr<=n&&du[rk[ptr]]<=lim){
            int id=rk[ptr];
            vis[id]=1;
            for(solid y:to[id]){
                hp[y.to].ins(y.val);
                to[y.to].pop_back();
            }
            ++ptr;
        }
        ans=0;
        for(reg i=ptr;i<=n;++i){
            int id=rk[i];
            if(!vis[id]){
                sol(id);
                ans+=f[id][1];
            }
        }
        for(reg i=ptr;i<=n;++i) vis[rk[i]]=0;
        printf("%lld\n",ans);
    }
    return 0;
}

}
signed main(){
    Miracle::main();
    return 0;
}

/*
   Author: *Miracle*
*/

 

转载于:https://www.cnblogs.com/Miracevin/p/10845258.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值