cunzai_zsy0531

关注我

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;
}
posted @ 2022-05-28 14:57  cunzai_zsy0531  阅读(21)  评论(0编辑  收藏  举报