【NOI2013模拟】秘密任务

Description:

【NOI2013模拟】秘密任务_第1张图片
对于 10 0% 的数据: 2 ≤ N ≤ 400, 1 ≤ M ≤ 4 00 0,1 ≤ T ≤ 5,1 ≤ Ai, c ≤ 10^9。无向图可能 有重边 。

题解:

首先建出最短路图,显然第二问就是跑个最小割吗?

第一问相当于问是否有大于一个最小割。

先跑一遍最大流,残量网络就分为三个部分:
1.超级源S能够走到的点。
2.能够走到超级汇T的点。
3.不属于以上两种的。

不显然结论:
若有条边(u,v),u、v分属于1、2部分,则(u,v)一定是任意一组最小割的割边。

这个证明非常简单。

假设(u,v)不能删,即给它的流量赋个inf。

那么现在S和T连通了。

我们必须要找到一条S->u的增广路或是v->T的增广路。

这个流量是不可能小于当前(u,v)的流量的,不然的话,一开始割的就是不是(u,v)了。

至此得证。

注意这题的坑点在于要拆点建图。

因为若有a[u]=a[v],且(u,v)是割集的一条边,其实是有两种割法的。

拆点就可以判掉了。

Code:

#include
#include
#define ll long long
#define fo(i, x, y) for(int i = x; i <= y; i ++)
#define fd(i, x, y) for(int i = x; i >= y; i --)
#define min(a, b) ((a) < (b) ? (a) : (b))
using namespace std;

const int N = 100005;

int Q, n, m, x, y, z;
int final[N], to[N], next[N], w[N], tot;
ll a[N];

void link(int x, int y, int z) {
    next[++ tot] = final[x], to[tot] = y, w[tot] = z, final[x] = tot;
    next[++ tot] = final[y], to[tot] = x, w[tot] = z, final[y] = tot;
}

int d[N * 100], bz[N], bx[N]; ll dis[N], dis2[N];

void spfa(int s, ll *dis) {
    fo(i, 1, n) dis[i] = 1e18;
    dis[s] = 0; d[d[0] = 1] = s; bz[s] = 1;
    fo(i, 1, d[0]) {
        int x = d[i];
        for(int j = final[x]; j; j = next[j]) {
            int y = to[j];
            if(dis[x] + w[j] < dis[y]) {
                dis[y] = dis[x] + w[j];
                if(!bz[y]) d[++ d[0]] = y, bz[y] = 1;
            }
        }
        bz[x] = 0;
    }
}

const int M = 1e6;
int b[405][405];

int S, T, co[M], dt[M], cur[M], tt, ts;

struct edge {
    int final[M], next[M], to[M], tot;
    ll r[M];
    void link(int x, int y, ll z) {
        next[++ tot] = final[x], to[tot] = y, r[tot] = z, final[x] = tot;
        next[++ tot] = final[y], to[tot] = x, r[tot] = 0, final[y] = tot;
    }
    void cl() {
        fo(i, 1, tt) final[i] = dt[i] = co[i] = cur[i] = 0;
        fo(i, 2, tot) next[i] = 0;
        tot = 1;
    }
} e;

ll dfs(int x, ll flow) {
    if(x == T) return flow;
    ll use = 0;
    for(int i = cur[x]; i; i = e.next[i], cur[x] = i) {
        int y = e.to[i];
        if(e.r[i] && dt[y] + 1 == dt[x]) {
            ll tmp = dfs(y, min(flow - use, e.r[i]));
            e.r[i] -= tmp, e.r[i ^ 1] += tmp, use += tmp;
            if(use == flow) return use;
        }
    }
    cur[x] = e.final[x];
    if(!(-- co[dt[x]])) dt[S] = tt;
    ++ co[++ dt[x]];
    return use;
}

void dg(int x) {
    if(bz[x]) return;
    bz[x] = 1;
    for(int i = e.final[x]; i; i = e.next[i])
        if(e.r[i]) dg(e.to[i]);
}

void dg2(int x) {
    if(bz[x]) return;
    bz[x] = 2;
    for(int i = e.final[x]; i; i = e.next[i])
        if(e.r[i ^ 1]) dg2(e.to[i]);
}


int main() {
    for(scanf("%d", &Q); Q; Q --) {
        fo(i, 1, tot) next[i] = 0;
        fo(i, 1, n) final[i] = 0;
        tot = 0;
        scanf("%d %d", &n, &m);
        fo(i, 1, n - 1) scanf("%lld", &a[i]);
        a[n] = 1e18;
        fo(i, 1, m) {
            scanf("%d %d %d", &x, &y, &z);
            link(x, y, z);
        }
        fo(i, 1, n) bz[i] = 0;
        spfa(1, dis); spfa(n, dis2);
        fo(i, 1, n) bx[i] = (dis[i] + dis2[i] == dis[n]);
        fo(i, 1, n) fo(j, 1, n) b[i][j] = 0;
        fo(x, 1, n) if(bx[x]) for(int j = final[x]; j; j = next[j])
            if(dis[x] + w[j] == dis[to[j]] && bx[to[j]])
                b[x][to[j]] ++;
        e.cl();
        S = 1; T = n; tt = T;
        fo(i, 1, n) fo(j, 1, n) if(b[i][j]) {
            tt ++;
            e.link(i, tt, (ll) b[i][j] * a[i]),
            e.link(tt, j, (ll) b[i][j] * a[j]);
        }
        co[0] = tt; ll ans = 0;
        for(; dt[S] < tt;) ans += dfs(S, 1LL << 62);
        fo(i, 1, tt) bz[i] = 0;
        dg(S); dg2(T);
        ll ans2 = 0;
        fo(i, 1, tt) for(int j = e.final[i]; j; j = e.next[j])
            if(bz[i] && bz[e.to[j]] && bz[i] != bz[e.to[j]])
                ans2 += e.r[j];
        if(ans == ans2) printf("Yes "); else printf("No ");
        printf("%lld\n", ans);
    }
}

你可能感兴趣的:(网络流,SPFA,杂题)