Codeforces Round #661 (Div. 3)E1. Weights Division (easy version)E2 Weights Division (hard version)

https://codeforces.com/contest/1399/problem/E1
https://codeforces.com/contest/1399/problem/E2
E1题意:给你一颗树,及树上每条边的值,此时你可以得到由根节点1到所有叶子节点的路径和sum,同时给你一个数s,你需要通过操作边权使得路径和sum<=s,每次操作可以选择任意一条边使得这条边除以2并向下取整。询问最小的操作次数。
思路:发现在将边权除以2时,需要考虑是先操作影响叶子节点多的边还是先操作权值大的边,这时候我们先通过dfs将每条边影响的叶子结点的数量和算出来,然后将其与边权相乘我们就得到了这条边对于总和sum的影响值ans,这样我们就可以通过优先队列每次找到影响最大的边并将其除以2,同时还有一个坑点,例如一条边权值为3,影响的叶子结点数量为6,此时将这条边除以2的话其影响值由36->16,减少了12,但如果只将上面的ans放入到优先队列中除2的话,仅减少了9,故我们应该将每条边操作后的减小值放入到优先队列中排序,这样才是最优解。
E2题意:在原本的基础上,为每条边在添加一个值1或2,每次操作这条边时需要消耗1或2,问消耗最小是多少。
思路:分别建两个优先队列一个存消耗为1的边的影响,另一个存消耗为2的边的影响,其他都与E1操作一致,每次删边考虑我删两次消耗为1的边与删一次消耗为2的边哪个更有效,这样就明确了贪心方法,在E1上做小的修改就可以AC了。
E1代码:

#include <bits/stdc++.h>
using namespace std;
#define pb push_back
#define debug(x) cout<<x<<endl
#define int long long 
const int mod=1000000007;
inline int read() {int num=0, w=0;char ch=0;while (!isdigit(ch)) {w|=ch=='-';ch = getchar();}while (isdigit(ch)) {num = (num<<3) + (num<<1) + (ch^48);ch = getchar();}return w? -num: num;}

// long long *long long 		**
struct node//存边
{
	int next;
	int v;
	int dian;
};
struct node1//存优先队列中的点
{
	int v;
	int sum;
	int cha;
	int kk;
	friend bool operator< (node1 n1, node1 n2)
    {
    	return n1.cha<n2.cha;
        
    }
};
vector<node> a[120000];//存树
map<int,int> mp;//标记哪个点是否走过
int qwe=0;
priority_queue <node1> q;
int dfs(int x){
	mp[x]=1;int ans=0;int flag=0;
	for(int i=0;i<a[x].size();i++){
		node k=a[x][i];int kk=0;
		if(mp[k.next]==0){
			kk+=dfs(k.next);
			ans+=kk;
			int cha=kk*k.v-k.v/2*kk;
			q.push(node1{k.v,kk*k.v,cha,kk});//每次将这条边的影响值放入优先队列中
			qwe+=kk*k.v;
			flag=1;
		}

	}
	if(flag==0){
		return 1;
	}
	else{
		return ans;
	}
	
}
void init(){
	while(q.size()){
			q.pop();
	}
	qwe=0;mp.clear();
}
signed main()
{
	int T ;
	T=read();
	while(T--){
		init();
		int n,m;
		n=read();m=read();
		for(int i=0;i<=n+10;i++){
			a[i].clear();
		}
		while(q.size()){
			q.pop();
		}
		for(int i=0;i<n-1;i++){
			int x,y,v,dian;
			x=read();y=read();v=read();dian=read();
			a[x].pb(node{y,v,dian});
			a[y].pb(node{x,v,dian});
		}
		int cishu=0;
		int k=dfs(1);
		while(qwe>m){
			node1 t=q.top();
			q.pop();
			qwe-=t.cha;
			int cha=(t.sum-t.cha)-(t.kk)*(t.v/2/2);
			q.push(node1{t.v/2,t.sum-t.cha,cha,t.kk});
			cishu++;
		}
		cout<<cishu<<endl;
	}
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值