Description
给出一张 n 个点 m 条边的无向图,每条边(ai,bi)有一个权值 wi 和费用 ci,表示这条边 每降低 1 的权值需要 ci 的花费。现在一共有 S 费用可以用来降低某些边的权值(可以降到 负数),求图中的一棵权值和最小的生成树并输出方案。
Solution
大水题?
先随便建一棵最小生成树,然后开始操作。
显而易见的是,我们必然会死磕一条边i将s花光(余数<ci)而不会组合边。
感性证明:
- 若ci最小,肯定只能减掉s/ci然后余数不足以操作其他边。
但如果不减掉s/ci而是分配一部分给其他c更大的边,说明减其他边会比减i更赚,那我为什么不直接将s全部用于减其他边,矛盾。 - 若ci不是最小,肯定只能减掉s/ci然后余数不管。(对于分配给c更大边上文已经论述)
对于剩下的余数,若我们分配给j(cj<ci),说明<ci的费用就可以造成>0的收益,那为什么不将那 ⌊ s / c i ⌋ ∗ c i \lfloor s/ci\rfloor *ci ⌊s/ci⌋∗ci的费用给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,sum−s/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−(wi−wj)))
完事了。
但是好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);
}