CF1399E2 Weights Division (hard version) 题解
对于两种权值的分别贪心维护,最后枚举一种权值选多少个,另一个二分出来,总复杂度 \(2\log\)。
点击查看代码
#include<cstdio>
#include<iostream>
#include<queue>
typedef long long ll;
inline int min(const int &a,const int &b){return a<b?a:b;}
const int N=1e5+13,M=4e6+13,INF=0x3f3f3f3f;
struct Edge{int v,w,c,nxt;}e[N<<1];
int n,h[N],tot,siz[N],val[N],cost[N];
ll S,a1[M],a2[M];
struct Node{
int x;
Node(int xx=0){x=xx;}
bool operator <(const Node &a)const{return ((ll)val[x]+1)/2*siz[x]<((ll)val[a.x]+1)/2*siz[a.x];}
};
std::priority_queue<Node> t1,t2;
inline void add_edge(int u,int v,int w,int c){e[++tot]=(Edge){v,w,c,h[u]};h[u]=tot;}
void dfs(int u,int fa){
bool son=0;
for(int i=h[u];i;i=e[i].nxt){
int v=e[i].v;if(v==fa) continue;
val[v]=e[i].w,cost[v]=e[i].c;
dfs(v,u);siz[u]+=siz[v],son=1;
}
if(!son) siz[u]=1;
}
int main(){int T;scanf("%d",&T);while(T--){
scanf("%d%lld",&n,&S);
for(int i=1;i<=n;++i) h[i]=siz[i]=0;tot=0;
for(int i=1;i<n;++i){
int u,v,w,c;scanf("%d%d%d%d",&u,&v,&w,&c);
add_edge(u,v,w,c),add_edge(v,u,w,c);
}
dfs(1,0);ll Sum=0;
for(int i=2;i<=n;++i){
Sum+=(ll)val[i]*siz[i];
if(cost[i]==1) t1.push(Node(i));
else t2.push(Node(i));
}
int cnt1=0,cnt2=0;
while(!t1.empty()){
int u=t1.top().x;t1.pop();
a1[++cnt1]=a1[cnt1-1]+((ll)val[u]+1)/2*siz[u];
val[u]>>=1;
if(val[u]) t1.push(Node(u));
}
while(!t2.empty()){
int u=t2.top().x;t2.pop();
a2[++cnt2]=a2[cnt2-1]+((ll)val[u]+1)/2*siz[u];
val[u]>>=1;
if(val[u]) t2.push(Node(u));
}
int ans=INF;
for(int i=0;i<=cnt1;++i){
if(Sum-a1[i]<=S){ans=min(ans,i);break;}
if(Sum-a1[i]-a2[cnt2]>S) continue;
int l=0,r=cnt2;
while(l<r){
int mid=(l+r)>>1;
if(Sum-a1[i]-a2[mid]<=S) r=mid;
else l=mid+1;
}
ans=min(ans,i+2*l);
}
printf("%d\n",ans);
}
return 0;
}