Topcoder SRM 720 Hard

链接:

link

题意:

给一个图和两棵树,你要让第一棵树是最小生成树,第二棵树是最大生成树,改变的边权之和最小。

题解:

网络流。
首先可以很容易地转化为一个一般性问题:
记新边权为 w w ,旧边权为v,有一些形如限制 wiwj w i ≤ w j 的限制,使 |wivi| ∑ | w i − v i | 最小。
fi(x)=|xvi| f i ( x ) = | x − v i | fi(x)=fi(x)fi(x1) f i ′ ( x ) = f i ( x ) − f i ( x − 1 ) ,注意到 fi(x) f i ′ ( x ) 是不降的。
考虑对于一个给定的 x x ,如何求出在最优解中哪些边的边权不超过x
将每个边变成网络流上的一个点,点权为 fi(x) f i ′ ( x ) ,同时如果 wiwj w i ≤ w j ,就从 i i j连一条边。
求出一个最小权闭合子图记为 C C ,那么C中的点最终的 w w 会大于等于x
下面给出证明:
假设存在 DC D ⊂ C ,同时 D D 中的点在最优解中小于x,注意到一定不会有 CD C − D D D 的边,这意味着CD也是一个闭合子图。
由于 C C 是最小权闭合子图,所以val(D)0,由于 fi(x) f i ′ ( x ) 不降,所以将 D D 的权值改为x不会劣。
最后实际上是 logmax(vi) l o g m a x ( v i ) 次网络流的复杂度。

代码:

#include <bits/stdc++.h>
#define xx first
#define yy second
#define mp make_pair
#define pb push_back
#define mset(x, y) memset(x, y, sizeof x)
#define mcpy(x, y) memcpy(x, y, sizeof x)
using namespace std;

typedef long long LL;
typedef pair <int, int> pii;

const int MAXN = 1005;
const int MAXM = 200005;
const int INF = 0x3f3f3f3f;

bool edg[MAXN][MAXN], tag[MAXN], chosen[MAXN];
int n, m, ql, qr, q[MAXN], dep[MAXN], w[MAXN];
vector <pii> adj[MAXN];
pii par[MAXN];
LL ans;

inline void Dfs(int x)
{
    for (auto e : adj[x])
        if (e.xx ^ par[x].xx)
            par[e.xx] = mp(x, e.yy), dep[e.xx] = dep[x] + 1, Dfs(e.xx);
}

inline void Add(int x, int y, int i, bool t)
{
    if (x == y)
        return ;
    if (dep[x] < dep[y])
    {
        if (!t)
            edg[par[y].yy][i] = 1;
        else
            edg[i][par[y].yy] = 1;
        Add(x, par[y].xx, i, t);
    }
    else
    {
        if (!t)
            edg[par[x].yy][i] = 1;
        else
            edg[i][par[x].yy] = 1;
        Add(par[x].xx, y, i, t);
    }
}

namespace Flow
{
    struct Edge
    {
        int p, v, w;
    } e[MAXM << 3];

    int e_cnt, S, T, V, ql, qr, q[MAXN], hed[MAXN], dis[MAXN], cur[MAXN];

    inline void Init(int n)
    {
        V = n;
        for (int i = 0; i < V; i ++)
            hed[i] = 0;
        e_cnt = 1;
    }

    inline void Addedge(int x, int y, int w)
    {
        e[++ e_cnt] = {y, hed[x], w}; hed[x] = e_cnt;
        e[++ e_cnt] = {x, hed[y], 0}; hed[y] = e_cnt;
    }

    inline bool Bfs()
    {
        for (int i = 0; i < V; i ++)
            dis[i] = 0;
        dis[q[ql = 0] = S] = qr = 1;
        while (ql ^ qr)
        {
            int x = q[ql ++];
            for (int i = hed[x]; i; i = e[i].v)
                if (e[i].w && !dis[e[i].p])
                    dis[q[qr ++] = e[i].p] = dis[x] + 1;
        }
        return dis[T];
    }

    inline int Dfs(int x, int f)
    {
        if (x == T)
            return f;
        int ret = 0, t = 0;
        for (int &i = cur[x]; i; i = e[i].v)
            if (e[i].w && dis[e[i].p] == dis[x] + 1)
            {
                t = Dfs(e[i].p, min(f, e[i].w));
                f -= t; ret += t; e[i].w -= t; e[i ^ 1].w += t;
                if (!f)
                    return ret;
            }
        return dis[x] = -1, ret;
    }

    inline int Dinic()
    {
        int ret = 0;
        while (Bfs())
        {
            for (int i = 0; i < V; i ++)
                cur[i] = hed[i];
            ret += Dfs(S, INF);
        }
        return ret;
    }
}

inline void Solve(int l, int r, vector <int> &cur)
{
    if (cur.size() == 0)
        return ;
    if (cur.size() == 1)
    {
        if (w[cur[0]] < l)
            ans += l - w[cur[0]];
        if (w[cur[0]] > r)
            ans += w[cur[0]] - r;
        return ;
    }
    if (l == r)
    {
        for (auto x : cur)
            ans += abs(w[x] - l);
        return ;
    }
    int n = cur.size(), mid = l + r >> 1;
    Flow::Init(n + 2);
    Flow::S = n, Flow::T = n + 1;
    for (int i = 0; i < cur.size(); i ++)
        if (w[cur[i]] > mid)
            Flow::Addedge(Flow::S, i, 1);
        else
            Flow::Addedge(i, Flow::T, 1);
    for (int i = 0; i < cur.size(); i ++)
        for (int j = 0; j < cur.size(); j ++)
            if (edg[cur[i]][cur[j]])
                Flow::Addedge(i, j, INF);
    Flow::Dinic();
    for (int i = 0; i < n; i ++)
        chosen[i] = Flow::dis[i];
    vector <int> p, q;
    for (int i = 0; i < n; i ++)
        if (chosen[i])
            p.pb(cur[i]);
        else
            q.pb(cur[i]);
    Solve(l, mid, q);
    Solve(mid + 1, r, p);
}

class ExtremeSpanningTrees
{
    public:
        LL minTime(vector <int> a, vector <int> b, vector <int> w, vector <int> m1, vector <int> m2) 
        {
            n = m1.size() + 1, m = a.size(), ans = 0;
            mset(edg, 0);
            for (int i = 0; i < m; i ++)
                ::w[i] = w[i];
            for (int i = 0; i < n; i ++)
                adj[i].clear();
            for (int i = 0; i < m; i ++)
                tag[i] = 0;
            for (int i = 0, u, v; i < n - 1; i ++)
                u = a[m1[i]], v = b[m1[i]], adj[u].pb(mp(v, m1[i])), adj[v].pb(mp(u, m1[i])), tag[m1[i]] = 1;
            par[0] = mp(-1, -1);
            Dfs(0);
            for (int i = 0; i < m; i ++)
                if (!tag[i])
                    Add(a[i], b[i], i, 0);
            for (int i = 0; i < n; i ++)
                adj[i].clear();
            for (int i = 0; i < m; i ++)
                tag[i] = 0;
            for (int i = 0, u, v; i < n - 1; i ++)
                u = a[m2[i]], v = b[m2[i]], adj[u].pb(mp(v, m2[i])), adj[v].pb(mp(u, m2[i])), tag[m2[i]] = 1;
            par[0] = mp(-1, -1);
            Dfs(0);
            for (int i = 0; i < m; i ++)
                if (!tag[i])
                    Add(a[i], b[i], i, 1);
            vector <int> cur;
            int l = w[0], r = w[0];
            for (int i = 0; i < m; i ++)
                cur.pb(i), l = min(l, w[i]), r = max(r, w[i]);
            Solve(l, r, cur);
            return ans;
        }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值