codeforces 733F. Drivers Dissatisfaction

来源:互联网 发布:淘宝一千零一夜从哪看 编辑:程序博客网 时间:2024/06/06 03:27

题目链接: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]);}
0 0