bzoj-3118 Orz the MST

题意:

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

每条边上有一个权值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;
}
时间: 2024-11-07 18:59:14

bzoj-3118 Orz the MST的相关文章

BZOJ 3118 Orz the MST 线性规划

题意:链接 方法:线性规划 解析: 这道题我也是orz了. 刚开始脑抽,仅仅是认为所有标号为1的边都比标号为0的边小即可. 后来与140142讨论了下发现我俩都犯好sb的错误=-= 这个图也是建了一阵子. 回忆之前做过的有关一个无向联通图中,一个边在最小生成树上,必要条件是该边构成的环中,这一边一定不是最大值. 这样的话,题中既然给了在树上的边. 并且很显然树上的边只能增加,不在树上的边只能减小. 所以我们不妨设Xi代表第i条边的变化值 那么我们可以重新显然定义代价Ci. 结合之前的结论 编号为

3118: Orz the MST(单纯形)

题目链接:http://www.lydsy.com:808/JudgeOnline/problem.php?id=3118 题意:给出一个图以及图中指定的n-1条边组成的生成树.每条边权值加1或者减去1都有相应的代价.求一个最小代价使得给出的边是最小生成树. 思路:对于每条非树边,必与某些树边形成环.设该非树边的权值为w2,某树边的权值为 w1.最后非树边增加x2,树边减少x1,那么w1-x1<=w2+x2.这样我们可以得到一些式子.代价也知道,这样就转化成线性规划问题.题目求的是最小值,我们可

BZOJ 2654 &amp; 玄学二分+MST

题意: 给一张图,边带权且带颜色黑白,求出一棵至少包含k条白边的MST SOL: 正常人都想优先加黑边或者是白边,我也是这么想的...你看先用白边搞一棵k条边的MST...然后维护比较黑边跟白边像堆一样慢慢往里面加...不过讲课的时候跟原题有点出入...这里只有k条边好像比较难维护... 正解也非常巧妙...首先如果有一棵MST,他所含白边数量多于k,那么我们如果可以适当增加白边的边权那么我们就可以减少它的边而且达到最优....想想很有道理你让我证明那有点日了狗了... 然后我们就二分白边的增加

BZOJ 2654: tree( 二分 + MST )

我们给白色的边增加权值 , 则选到的白色边就会变多 , 因此可以二分一下. 不过这道题有点小坑... ------------------------------------------------------------------------------------------- #include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #define rep( i

【BZOJ 2654】 MST

2654: tree Description 给你一个无向带权连通图,每条边是黑色或白色.让你求一棵最小权的恰好有need条白色边的生成树. 题目保证有解. Input 第一行V,E,need分别表示点数,边数和需要的白色边数. 接下来E行,每行s,t,c,col表示这边的端点(点从0开始标号),边权,颜色(0白色1黑色). Output 一行表示所求生成树的边权和. V<=50000,E<=100000,所有数据边权为[1,100]中的正整数. Sample Input 2 2 1 0 1

BZOJ 2429: [HAOI2006]聪明的猴子( MST )

水题, 求MST即可. -------------------------------------------------------------------------------- #include<bits/stdc++.h> using namespace std; #define sqr(x) ((x) * (x)) const int maxn = 1009; struct edge { int u, v; double w; bool operator < (const e

BZOJ 1196: [HNOI2006]公路修建问题( MST )

水题... 容易发现花费最大最小即是求 MST 将每条边拆成一级 , 二级两条 , 然后跑 MST . 跑 MST 时 , 要先加 k 条一级road , 保证满足题意 , 然后再跑普通的 MST . ------------------------------------------------------------------------------------ #include<cstdio> #include<cstring> #include<algorithm&

BZOJ 1601: [Usaco2008 Oct]灌水( MST )

MST , kruskal 直接跑 ---------------------------------------------------------------------- #include<cstdio> #include<algorithm> #include<vector> #include<cstring> #include<iostream> #define rep( i , n ) for( int i = 0 ; i <

BZOJ 1083: [SCOI2005]繁忙的都市(MST)

裸的最小生成树..直接跑就行了 ---------------------------------------------------------------------- #include<cstdio> #include<algorithm> #include<vector> #define rep(i,n) for(int i=0;i<n;i++) #define addEdge(u,v,w) MST.edges.push_back((KRUSKAL::Ed