题意:
给出一个无向连通图,并指定其中一颗生成树;
每条边上有一个权值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;
}