E2. Weights Division (hard version)

题:https://codeforces.com/contest/1399/problem/E2

题意:给定一棵树,每个边权有w值和id值,分别代表边权和将改边权降为w/2的代价(1<=id<=2),当前代价nowsum为根节点到叶子节点路径的总和,问要经过最少多少id代价能将nowsum降为<=S;

分析:首先我们可以先把nowsum算出来,其次考虑用id=1和id=2来降nowsum;

   假设我取了x个id=1和y个id=2,那么肯定是选前x大w的id=1,和y大的id2;

   基于这样考虑,我们可以先预处理id=1和id=2move操作效益大往小的前缀和序列sum1[],sum2[];

   这样就可以枚举sum1来算sum2的位置来更新答案(nlogn)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define pb push_back
#define MP make_pair
#define pil pair<int,ll>
const int inf=0x3f3f3f3f;
const ll INF=1e18;
const int M=4e6+5;
struct edge{
    int v;
    ll w;
    int id;
};
vector<edge>g[M];
ll nowsum,sz[M],sum1[M],sum2[M];
struct node{
    ll x,y,z;
    bool operator < (const node &b)const{
        return x<b.x;
    }
};
priority_queue<node>que,que2;
void dfs1(int u,int fa){
    if(g[u].size()==1)
        sz[u]=1;
    for(auto it:g[u]){
        int v=it.v;
        ll w=it.w;
        if(v!=fa){
            dfs1(v,u);
            sz[u]+=sz[v];
            if(it.id==1)
                que.push(node{(w+1)/2*sz[v],w,sz[v]});
            else
                que2.push(node{(w+1)/2*sz[v],w,sz[v]});
            nowsum+=w*sz[v];
        }
    }
}
int main() {
    int T;
    scanf("%d",&T);
    while(T--){
        int n;
        ll S;
        scanf("%d%lld",&n,&S);
        nowsum=0;
        for(int i=1;i<=n;i++)
            g[i].clear(),sz[i]=0;
        while(!que.empty())
            que.pop();
        while(!que2.empty())
            que2.pop();
        for(int i=1;i<n;i++){
            int u,v,id;
            ll w;
            scanf("%d%d%lld%d",&u,&v,&w,&id);
            g[u].pb(edge{v,w,id});
            g[v].pb(edge{u,w,id});
        }
        dfs1(1,0);
        int tot1=0;
        while(que.size()>0){
            node u=que.top();
            que.pop();
            if(u.x==0)
                break;
            sum1[++tot1]=u.x;
            u.y=u.y/2;
            u.x=(u.y+1)/2*u.z;
            que.push(u);
        }
        for(int i=1;i<=tot1;i++)
            sum1[i]+=sum1[i-1];
        int tot2=0;
        while(que2.size()>0){
            node u=que2.top();
            que2.pop();
            if(u.x==0)
                break;
            sum2[++tot2]=u.x;
            u.y=u.y/2;
            u.x=(u.y+1)/2*u.z;
            que2.push(u);
        }
        for(int i=1;i<=tot2;i++)
            sum2[i]+=sum2[i-1];
        sum2[tot2+1]=INF;
        ll need=nowsum-S;
        int ans=inf;
        for(int i=0;i<=tot1;i++){
            ll tmp=need-sum1[i];
            int pos=lower_bound(sum2,sum2+1+tot2,tmp)-sum2;
            if(pos==tot2+1)
                continue;
            ans=min(ans,i+2*pos);
        }
        printf("%d
",ans);
    }
    return 0;
}
View Code
原文地址:https://www.cnblogs.com/starve/p/13615938.html