题目链接:http://codeforces.com/contest/733/problem/F
题目大意:给一张n个点m条边的连通图,每条边(ai,bi)有一个权值wi和费用ci,表示这条边每降低1的权值需要ci的花费。现在一共有S费用可以用来降低某些边的权值(可以降到负数),求图中的一棵权值和最小的生成树并输出方案。
数据范围:2 ≤ n ≤ 2·10^5, n - 1 ≤ m ≤ 2·10^5, 1 ≤ wi, ci, S ≤ 10^9
题解:感觉我已经在水题的道路上一去不复返了hhhh。由于wi可以是负数,因此对于一棵生成树,我们只要把S全部用在ci最小的边即可。求出原图中的一棵最小生成树以及树中ci最小的边,计算出当前权值和。枚举每条不在生成树上的边 j (cj < ci),用lca求出aj到bj路径上w最大的边,用wj替换它然后计算出权值和,如果比答案小就更新答案。最后输出方案即可。
时间复杂度O(nlogn+mlogn)
代码如下:
#include <algorithm>
#include <cstdio>
const int N=200005;
int f[N][20],g[N][20],fa[N],w[N],c[N],a[N],b[N],id[N],fi[N],
to[N*2],ne[N*2],v[N*2],dep[N],u[N],n,m,tot,mn;
long long sum,ans;
bool cmp(int x,int y){return w[x]<w[y];}
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
void add(int x,int y,int z){
to[++tot]=y;v[tot]=z;ne[tot]=fi[x];fi[x]=tot;
}
void dfs(int x){
dep[x]=dep[f[x][0]]+1;
for (int i=f[x][0],j=0;f[i][j];i=f[i][j++]){
f[x][j+1]=f[i][j];
if (w[g[i][j]]>w[g[x][j]]) g[x][j+1]=g[i][j];
else g[x][j+1]=g[x][j];
}
for (int i=fi[x];i;i=ne[i])
if (to[i]!=f[x][0]){
f[to[i]][0]=x;
g[to[i]][0]=v[i];
dfs(to[i]);
}
}
int lca(int x,int y){
int z=0;
if (dep[x]<dep[y]) std::swap(x,y);
for (int i=17;dep[x]>dep[y];x=f[x][i]){
for (;i && dep[f[x][i]]<dep[y];i--);
if (w[g[x][i]]>w[z]) z=g[x][i];
}
for (int i=17;x!=y;x=f[x][i],y=f[y][i]){
for (;i && f[x][i]==f[y][i];i--);
if (w[g[x][i]]>w[z]) z=g[x][i];
if (w[g[y][i]]>w[z]) z=g[y][i];
}
return z;
}
int main(){
scanf("%d%d\n",&n,&m);
for (int i=1;i<=m;i++) scanf("%d",&w[i]);
for (int i=1;i<=m;i++) scanf("%d",&c[i]);
for (int i=1;i<=m;i++) scanf("%d%d",&a[i],&b[i]);
for (int i=1;i<=m;i++) id[i]=i;
std::sort(id+1,id+m+1,cmp);
for (int i=1;i<=n;i++) fa[i]=i;
sum=0;c[mn=0]=2000000000;
for (int i=1,j=id[1];i<=m;j=id[++i])
if (find(a[j])!=find(b[j])){
fa[find(a[j])]=find(b[j]);
sum+=w[j];u[j]=1;
add(a[j],b[j],j);
add(b[j],a[j],j);
if (c[j]<c[mn]) mn=j;
}
long long S;
scanf("%I64d\n",&S);
ans=sum-S/c[mn];
dfs(1);
int minc=c[mn];
for (int i=1;i<=m;i++)
if (c[i]<minc){
int x=lca(a[i],b[i]);
long long cur=sum+w[i]-w[x]-S/c[i];
if (cur<ans) ans=cur,mn=i;
}
printf("%I64d\n",ans);
w[mn]-=S/c[mn];u[lca(a[mn],b[mn])]=0;u[mn]=1;
for (int i=1;i<=m;i++)
if (u[i]) printf("%d %d\n",i,w[i]);
}