bzoj-3118 Orz the MST

来源:互联网 发布:淘宝国际快递转运 编辑:程序博客网 时间:2024/06/06 08:33

题意:

给出一个无向连通图,并指定其中一颗生成树;

每条边上有一个权值vali,如果增大这个权值1则花费Ai,减小1花费Bi;

现在要使指定的生成树为一颗最小生成树,求最小花费;

n<=300,m<=1000;


题解:

一道线性规划比较神的题目,前面刷的比较偏水就不刷了;

首先有一点极为显然的东西(我居然没看出来),树上的边一定减小权值,非树上的边一定增大权值;

然后考虑对于一颗生成树要为最小要满足的条件,也就是本题的约束条件;

如同Tarjan算法一样,每一条非树边都会在树上生成一个环;

而如果这个环上的某个边权值比它大,那么这个环就可以从那条边处断开,且生成树更小;

也就是说对于一个非树边,环上边都要小于等于它;

找约束的过程我写的似乎比较蠢,深搜记了一堆再LCA;

不过无论如何最坏也不过n*m总归不会因此TLE;

设xi为第i条边的改变量,那么树边的即为减小量,非树边的为增大量;

可得方程:vali+xi<=valj-xj   (i为树边j为非树边,且i,j在一个环上);

移项:xi+xj<=valj-vali;

现在的线性规划为:

Min∑costi*xi

xi+xj<=valj-vali

显然它不存在基本可行解,那么做一些变形;

首先将目标函数取反:

Max∑ -costi*xi

xi+xj<=valj-vali

然后对偶原理!

Max∑(valj-vali)*x(j,i)

%*&^%*>=-costi

再对不等式变号:

Max∑(valj-vali)*x(j,i)

-%*&^%*<=costi

现在就可以发现,线性规划已经是标准型了;

而题中的cost都是正数,也就是这个线性规划有基本可行解咯;

然后上单纯型直接搞这东西,题目性质也保证了这东西不会无界;

时间复杂度O(单纯型(n*m,m));

显然是会T死的,然而约束并不会有最坏情况n*m这么多。。

所以还是可以过的啦,n*m的数组开到4000能A;


代码:


#include<vector> #include<math.h> #include<stdio.h> #include<string.h> #include<algorithm> #define N 310 #define M 1100 using namespace std; const double EPS = 1e-8; const double INF = 1e100; struct node {     int n, m;     double a[M][M << 2], b[M], c[M << 2], v;     int find()     {         for (int i = 1; i <= n; i++)         {             if (c[i] > EPS)                 return i;         }         return 0;     }     void Rotate(int l, int e)     {         b[l] /= a[l][e];         for (int i = 1; i <= n; i++)         {             if (i != e)                 a[l][i] /= a[l][e];         }         a[l][e] = 1 / a[l][e];         for (int i = 1; i <= m; i++)         {             if (i == l || fabs(a[i][e]) < EPS)   continue;             b[i] -= b[l] * a[i][e];             for (int j = 1; j <= n; j++)             {                 if (j != e)                     a[i][j] -= a[l][j] * a[i][e];             }             a[i][e] *= -a[l][e];         }         v += c[e] * b[l];         for (int i = 1; i <= n; i++)         {             if (i != e)                 c[i] -= c[e] * a[l][i];         }         c[e] *= -a[l][e];     }     void Simplex()     {         int i, j, k, l, e;         while (e = find())         {             double lim = INF;             for (i = 1; i <= m; i++)             {                 if (a[i][e] < EPS)   continue;                 if (lim>b[i] / a[i][e])                     lim = b[i] / a[i][e], l = i;             }             Rotate(l, e);         }     } }T; vector<int>to[N], val[N], cost[N], no[N]; vector<bool>cov[N]; int fa[N], prec[N], prev[N], preno[N], tim[N], deep[N], tot; void Build(int x, int y, int val, int no) {     if (deep[x] < deep[y])         swap(x, y);     while (deep[x]>deep[y])     {         T.c[++T.n] = -(val - prev[x]);         T.a[no][T.n] = 1;         T.a[preno[x]][T.n] = 1;         x = fa[x];     }     while (x != y)     {         T.c[++T.n] =-( val - prev[x]);         T.a[no][T.n] = 1;         T.a[preno[x]][T.n] = 1;         x = fa[x];         T.c[++T.n] = -(val - prev[y]);         T.a[no][T.n] = 1;         T.a[preno[y]][T.n] = 1;         y = fa[y];     } } void dfs(int x, int pre, int v, int num, int d) {     int i, y;     fa[x]=pre,prev[x] = v, preno[x] = num, tim[x] = ++tot, deep[x] = d;     for (i = 0; i < to[x].size(); i++)     {         if (cov[x][i]&&(y=to[x][i])!=pre)             dfs(y, x, val[x][i], no[x][i], d + 1);     }     for (i = 0; i < to[x].size(); i++)     {         if (!cov[x][i] && tim[y = to[x][i]] && tim[y] < tim[x])         {             Build(x, y, val[x][i], no[x][i]);         }     } } int main() {     int n, m, i, j, k, x, y, v, f, a, b;     scanf("%d%d", &n, &m);     for (i = 1; i <= m; i++)     {         scanf("%d%d%d%d%d%d", &x, &y, &v, &f, &a, &b);         to[x].push_back(y), val[x].push_back(v), cov[x].push_back(f), no[x].push_back(i);         to[y].push_back(x), val[y].push_back(v), cov[y].push_back(f), no[y].push_back(i);         if (f)             T.b[i] = b;         else            T.b[i] = a;     }     dfs(1, 0, 0, 0, 1);     T.m=m;     T.Simplex();     printf("%.0lf",T.v);     return 0; }



0 0