1. 程式人生 > 實用技巧 >E2. Weights Division (hard version)

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\n",ans); } return 0; }
View Code