题解:CF1399E1 Weights Division (easy version)

题目链接

很有意思的一道题,题解里面大部分都是链式前向星来存且年代久远,这里写一篇 vector 的。

分析

题目的要求很简单,选取一条边使其权值 \(w\) 变为 \(\left \lfloor \frac{w}{2} \right \rfloor\),最终使根节点到所有叶子结点的路径之和不大于 \(S\)

解法

观察到可以将所有边的权值 \(\times\) 该边的使用次数来进行比较,判断优先级。
简单举个例子理解一下,如下图。
举例图片
假如我要把数字控制在 \(2999\) 内,观察到 \(6 \times 2 < 3000 \times 1\)
\((2,4)\) 两条边进行处理明显比处理 \(4,5\) 的公共父边 \((1,2)\) 更优。(毕竟 \(6\) 再怎么删也到不了负数)。

那么按照我们的策略,就可以轻松完成这道题了
(具体见下面优先队列)。

处理

既然要统计每条边的使用次数,那 dfs 一遍即可。
此时我们可以对每对点(每条边)映射一个值 \(i\) (在读入时处理)。
定义 \(f[i]\) 表示第 \(i\) 条边的使用次数。
dfs 如下。

void dfs(int u,int fa){
	bool flag=0;
	for(int i=0;i<g[u].size();i++){
		int v=g[u][i];
		if(v!=fa){
			dfs(v,u);
			f[G[u][v]]=dp[v];
			dp[u]+=dp[v];
			flag=1;
		}
	}
	if(!flag){
		dp[u]=1;
	}
}

接下来就是要初始化所有的边权 \(\times\) 次数,这时可以使用优先队列处理。同时把现在的路径之和 \(sum\) 求出来。

这里来解释一下排序策略,要让一个贡献更大的排在前面。
这里的贡献就是在边权除二后使 \(sum\) 的减小量。
计算式为 \((w - w \div 2) \times f[i]\)

//结构体存储
struct Node{
    int id,w;
  	bool operator < (const Node &nxt) const{
        return (w-w/2)*f[id]<(nxt.w-nxt.w/2)*f[nxt.id];
	}
};
priority_queue <Node> q; 
//统计
for(int i=1;i<n;i++){
    q.push((Node){i,w[i]});
    sum+=f[i]*w[i];
}

之后不断的减小 \(sum\) 直到 \(sum \le S\)

while(sum>S){
	Node cur=q.top();
	q.pop();
	sum-=(cur.w-cur.w/2)*f[cur.id];
	ans++;
	q.push((Node){cur.id,cur.w/2});
}

最后注意一下多组数据记得清空。

完整代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
inline int read(){
	int x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9'){
		if(ch=='-'){
			f=-1;
		}
		ch=getchar();
	}
	while(ch>='0'&&ch<='9'){
		x=x*10+ch-'0';
		ch=getchar();
	}
	return x*f;
}
void write(int x){
	if(x<0){
		putchar('-');
		x=-x;
	}
	if(x>9){
		write(x/10);
	}
	putchar(x%10+'0');
	return ;
}
const int N=1e5+5;
vector<int> g[N];
map<int,int> G[N];
int f[N],w[N];
int T,n,S,sum,ans,dp[N];
struct Node{
    int id,w;
	bool operator < (const Node &nxt) const{
    	return (w-w/2)*f[id]<(nxt.w-nxt.w/2)*f[nxt.id];
	}
};
priority_queue <Node> q; 
void dfs(int u,int fa){
	bool flag=0;
	for(int i=0;i<g[u].size();i++){
		int v=g[u][i];
		if(v!=fa){
			dfs(v,u);
			f[G[u][v]]=dp[v];
			dp[u]+=dp[v];
			flag=1;
		}
	}
	if(!flag){
		dp[u]=1;
	}
}
signed main(){
	T=read();
	while(T--){
		n=read();S=read();
        //记得清空
		while(!q.empty()){
			q.pop();
		}
		sum=ans=0;
		for(int i=1;i<=n;i++){
			g[i].clear();
			G[i].clear();
			f[i]=w[i]=dp[i]=0;
		}
        //读入+映射i
		int u,v;
		for(int i=1;i<n;i++){
			u=read();v=read();w[i]=read();
			g[u].push_back(v);
			g[v].push_back(u);
			G[u][v]=G[v][u]=i;//注意这里的映射,记得u到v和v到u都要映射
		}
		dfs(1,0);
		for(int i=1;i<n;i++){
			q.push((Node){i,w[i]});
			sum+=f[i]*w[i];
		}
		while(sum>S){
			Node cur=q.top();
			q.pop();
			sum-=(cur.w-cur.w/2)*f[cur.id];
			ans++;
			q.push((Node){cur.id,cur.w/2});
		}
		write(ans);
		puts("");
	}
	return 0;
}
posted @ 2025-09-23 21:08  ftzx  阅读(5)  评论(0)    收藏  举报