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;
}