CF733F Drivers Dissatisfaction

Description

给出一张 n 个点 m 条边的无向图,每条边(ai,bi)有一个权值 wi 和费用 ci,表示这条边 每降低 1 的权值需要 ci 的花费。现在一共有 S 费用可以用来降低某些边的权值(可以降到 负数),求图中的一棵权值和最小的生成树并输出方案。


Solution

大水题?

先随便建一棵最小生成树,然后开始操作。
显而易见的是,我们必然会死磕一条边i将s花光(余数<ci)而不会组合边。
感性证明:

  1. 若ci最小,肯定只能减掉s/ci然后余数不足以操作其他边。
    但如果不减掉s/ci而是分配一部分给其他c更大的边,说明减其他边会比减i更赚,那我为什么不直接将s全部用于减其他边,矛盾。
  2. 若ci不是最小,肯定只能减掉s/ci然后余数不管。(对于分配给c更大边上文已经论述)
    对于剩下的余数,若我们分配给j(cj<ci),说明<ci的费用就可以造成>0的收益,那为什么不将那 ⌊ s / c i ⌋ ∗ c i \lfloor s/ci\rfloor *ci s/cici的费用给j呢,矛盾。

所以我们会直接独轮车冲蝗怼一条边。
那就简单了。
先算出最小生成树总权值sum。
3. 对于树边直接 a n s = m i n ( a n s , s u m − s / c i ) ans=min(ans,sum-s/c_i) ans=min(ans,sums/ci)
4. 对于非树边要在树上找到x-y路径上最大的一条边j(倍增/树剖),然后用i替换j,此时 a n s = m i n ( a n s , s u m − ( s / c i − ( w i − w j ) ) ) ans=min(ans,sum-(s/c_i-(w_i-w_j))) ans=min(ans,sum(s/ci(wiwj)))

完事了。
但是好tm难调aaa


Code

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
struct data{
	int y,z,nxt,id;
}e[400010];
struct edge{
	int x,y,z,c,id;
}a[400010];
int dep[200010];
int head[200010];
int maxn[200010][19];
int fa[200010][19];
bool vis[200010];
int f[200010];
int n,m,s,cnt,ed,dt;
ll ans=1e18,sum;
void add(int x,int y,int z,int id){
	cnt++;
	e[cnt].y=y;
	e[cnt].z=z;
	e[cnt].id=id;
	e[cnt].nxt=head[x];
	head[x]=cnt;
}
bool cmp(edge u,edge v){
	return u.z<v.z;
}
int wmax(int x,int y){
	return a[x].z>a[y].z?x:y;
}
int find(int x){
	return x!=f[x]?f[x]=find(f[x]):x;
}
void dfs(int x){
	for(int i=1;i<=18;i++){
		fa[x][i]=fa[fa[x][i-1]][i-1];
		maxn[x][i]=wmax(maxn[x][i-1],maxn[fa[x][i-1]][i-1]);
	}
	for(int i=head[x];i;i=e[i].nxt){
		int y=e[i].y,z=e[i].z,id=e[i].id;
		if(y==fa[x][0]) continue;
		dep[y]=dep[x]+1;
		maxn[y][0]=id;
		fa[y][0]=x;
		dfs(y);
	}
}
int get(int x,int y){
	int ans=0;
	if(dep[x]<dep[y]) swap(x,y);
	for(int i=18;i>=0;i--)
	if(dep[fa[x][i]]>=dep[y]){
		ans=wmax(ans,maxn[x][i]);
		x=fa[x][i];
	}
	if(x==y) return ans;
	for(int i=18;i>=0;i--)
	if(fa[x][i]!=fa[y][i]){
		ans=wmax(ans,maxn[x][i]);
		ans=wmax(ans,maxn[y][i]);
		x=fa[x][i],y=fa[y][i];
	}
	ans=wmax(ans,maxn[x][0]);
	ans=wmax(ans,maxn[y][0]);
	return ans;
}
void kruskal(){
	sort(a+1,a+m+1,cmp);
	for(int i=1;i<=n;i++) f[i]=i;
	for(int i=1;i<=m;i++){
		int x=find(a[i].x);
		int y=find(a[i].y);
		if(x==y) continue;
		add(a[i].x,a[i].y,a[i].z,i);
		add(a[i].y,a[i].x,a[i].z,i);
		sum+=a[i].z;
		vis[i]=true;
		f[x]=y;
	}
}
int main(){
	int x,y,z;
	dep[1]=1; 
	a[0].z=-2e9;
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++){
		scanf("%d",&a[i].z);
		a[i].id=i;
	}
	for(int i=1;i<=m;i++)
	scanf("%d",&a[i].c);
	for(int i=1;i<=m;i++)
	scanf("%d%d",&a[i].x,&a[i].y);
	scanf("%d",&s);
	kruskal(),dfs(1);
	ans=sum;
	for(int i=1;i<=m;i++){
		//cout<<i<<endl;
		if(vis[i]){
			if(ans>sum-s/a[i].c){
				ans=sum-s/a[i].c;
				ed=i,dt=0;
			}
			continue;
		}
		x=get(a[i].x,a[i].y);
		if(x==0)
		{
			puts("FUCK!!! ldl loves hyq!");
		}
		if(a[i].z-a[x].z<=s/a[i].c&&ans>sum-(s/a[i].c-(a[i].z-a[x].z))){
			ans=sum-(s/a[i].c-(a[i].z-a[x].z));
			ed=i,dt=x;
		}
	}
	printf("%lld\n",ans);
	vis[ed]=1,vis[dt]=0;
	if(ed!=0) a[ed].z-=s/a[ed].c;
	for(int i=1;i<=m;i++)
	if(vis[i]) printf("%d %d\n",a[i].id,a[i].z);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值