ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【JZOJ3348】【NOI2013模拟】秘密任务 (Standard IO) (最小割唯一性的判定)

2019-07-10 21:02:08  阅读:197  来源: 互联网

标签:tmp return ll rest Standard vis IO NOI2013 dis


题目大意

给出一个无向图,一个人会从1号点沿着最短路走到n号点(可能有多条路径),而你需要在某些边的一端设置障碍,使他最终不能够到达n号点,求最小代价,并判断方案是否唯一。

分析

首先建出最短路图。我们可以把原来的边\((u,v)\)拆成两段:\((u,x)\)和\((x,v)\);割掉\((u,x)\)的代价为\(a[u]\),割掉\((x,v)\)的代价为\(a[v]\)。我们的目的是用最小代价割掉最短路图上某些边,使得\(1\)走不到\(n\),这是个最小割模型,于是做一遍最大流就求出了第二问。

问题难在第一问,如何判断最小割是否唯一?

一个不是很显然的结论是:设残量网络中源点\(S\)通过未满流边能够到达的点集为\(S_0\),通过未满流边能够到达汇点\(T\)的点集为\(T_0\),若一条边\((u,v)\)满足\(u\in S_0,v\in T_0\),这条边必定存在于任意一个最小割中。

反证法证明:假设\((u,v)\)不能割,那可以将它的容量视为无穷大,根据题设,我们又能增加\(S\)到\(T\)的流量,这与已经求得最大流矛盾,故\((u,v)\)是必须要割的边。

有了这个结论,我们直接将必割边的代价加起来,如果等于所求最小割,那么方案唯一,否则方案不唯一。

Code

#include <cstdio>
#include <cstring>

typedef long long ll;
const int N = 100007, M = 100007;
const ll INF = (1ll << 60);
ll min(ll a, ll b) { return a < b ? a : b; }

int test;
int n, m, S, T, cnt, s1[N], s2[N];
ll ans, sum, dis[N], a[N], len[M];
int tot, st[N], to[M], nx[M];
int total, head[N], go[M], next[M];
int h, t, q[N], vis[N];
void clear1() { tot = 0; memset(st, 0, sizeof(st)); memset(to, 0, sizeof(to)); memset(nx, 0, sizeof(nx)); }
void clear2() { total = 0; memset(head, 0, sizeof(head)); memset(go, 0, sizeof(go)); memset(next, 0, sizeof(next)); }
void add(int u, int v, ll w) { to[++tot] = v, nx[tot] = st[u], len[tot] = w, st[u] = tot; }
void link(int u, int v) { go[++total] = v, next[total] = head[u], head[u] = total; }

void spfa()
{
    memset(dis, 0x3f, sizeof(dis));
    memset(vis, 0, sizeof(vis));
    h = 1, q[t = 1] = 1, vis[1] = 1, dis[1] = 0;
    while (h <= t)
    {
        int u = q[h++];
        vis[u] = 0;
        for (int i = st[u]; i; i = nx[i])
            if (dis[u] + len[i] < dis[to[i]])
            {
                dis[to[i]] = dis[u] + len[i];
                if (!vis[to[i]]) q[++t] = to[i], vis[to[i]] = 1;
            }
    }
}
void dfs(int u)
{
    vis[u] = 1;
    for (int i = st[u]; i; i = nx[i])
        if (dis[to[i]] + len[i] == dis[u])
        {
            link(u, to[i]);
            if (!vis[to[i]]) dfs(to[i]);
        }
}
int bfs()
{
    memset(dis, 0, sizeof(dis));
    h = 1, q[t = 1] = S, dis[S] = 1;
    while (h <= t)
    {
        int u = q[h++];
        for (int i = st[u]; i; i = nx[i])
            if (!dis[to[i]] && len[i] > 0)
            {
                dis[to[i]] = dis[u] + 1, q[++t] = to[i];
                if (to[i] == T) return 1;
            }
    }
    return 0;
}
ll dinic(int u, ll flow)
{
    if (u == T) return flow;
    ll rest = flow, tmp;
    for (int i = st[u]; i; i = nx[i])
        if (dis[to[i]] == dis[u] + 1 && len[i] > 0)
        {
            tmp = dinic(to[i], min(rest, len[i]));
            if (!tmp) dis[to[i]] = 0;
            len[i] -= tmp, len[i ^ 1] += tmp, rest -= tmp;
            if (!rest) return flow;
        }
    return flow - rest;
}
void getset()
{
    memset(s1, 0, sizeof(s1)); memset(s2, 0, sizeof(s2));
    memset(vis, 0, sizeof(vis));
    h = 1, q[t = 1] = S, vis[S] = 1;
    while (h <= t)
    {
        int u = q[h++]; s1[u] = 1;
        for (int i = st[u]; i; i = nx[i]) if (len[i] > 0 && !vis[to[i]]) q[++t] = to[i], vis[to[i]] = 1;
    }
    memset(vis, 0, sizeof(vis));
    h = 1, q[t = 1] = T, vis[T] = 1;
    while (h <= t)
    {
        int u = q[h++]; s2[u] = 1;
        for (int i = st[u]; i; i = nx[i]) if (len[i ^ 1] > 0 && !vis[to[i]]) q[++t] = to[i], vis[to[i]] = 1;
    }
}

int main()
{
    scanf("%d", &test);
    while (test--)
    {
        clear1(), clear2();
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= n - 1; i++) scanf("%lld", &a[i]); a[n] = INF;
        for (int i = 1, u, v, w; i <= m; i++) scanf("%d%d%d", &u, &v, &w), add(u, v, w), add(v, u, w);
        spfa();
        memset(vis, 0, sizeof(vis)), dfs(n);
        clear1(); tot = 1, cnt = n;
        for (int i = 1; i <= n; i++) for (int j = head[i]; j; j = next[j]) add(go[j], ++cnt, a[go[j]]), add(cnt, go[j], 0), add(cnt, i, a[i]), add(i, cnt, 0);
        S = 1, T = n, ans = sum = 0;
        while (bfs()) ans += dinic(S, INF);
        getset();
        cnt = n;
        for (int i = 1; i <= n; i++)
            for (int j = head[i]; j; j = next[j])
            {
                ++cnt;
                if (s1[go[j]] && s2[cnt]) sum += a[go[j]];
                if (s1[cnt] && s2[i]) sum += a[i];
            }
        printf(sum == ans ? "Yes %lld\n" : "No %lld\n", ans);
    }
    return 0;
}

标签:tmp,return,ll,rest,Standard,vis,IO,NOI2013,dis
来源: https://www.cnblogs.com/zjlcnblogs/p/11166405.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有