题目

给出一个 $n$ 个顶点 $m$ 条边的图,要求阻塞一些边,使得从 $1$ 到 $n$ 的最短路变长,求阻塞的边长度和的最小值,不必保证阻塞后可达。

分析

很显然,要阻塞的边肯定在最短路图上,先跑一遍单源最短路,求出最短路图。

要使最短路变长,肯定要同时切断原有的所有最短路,又要是长度(相当于流量)和最小,很容易想到就是求最小割。

简而言之,就是在最短路图上求最小割。

两个模板拼起来就好了(难得写抄这么长的能一遍AC)

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const ll INF = 1ll << 60;
const int INF2 = 0x3f3f3f3f;
const int maxv = 10000+10;        //最大顶点数
const int maxn = maxv;
const int maxe = 10000+10;        //最大边数
ll dis[maxv];
int cnt[maxv];
bool inq[maxv];                //记录是否在队中
int head[maxv], id; //head2[maxv], cnt2;
int n, m;

struct Edge
{
    int to, w, next;
}edge[maxe];

inline void init()
{
    memset(head, -1, sizeof(head));
    id=0;
}

inline void addedge(int u, int v, int w, int id)
{
    edge[id].to = v;
    edge[id].w = w;
    edge[id].next = head[u];
    head[u] = id;
}

bool SPFA(int s)
{
    deque<int>q;
    memset(inq, false, sizeof(inq));
    memset(cnt, 0, sizeof(cnt));
    for (int i = 1; i <= n; i++)  dis[i] = INF;
    dis[s] = 0;
    inq[s] = true;
    q.push_back(s);

    while (!q.empty())
    {
        int u = q.front(); q.pop_front();
        inq[u] = false;
        for (int i = head[u]; i != -1; i = edge[i].next)
        {
            int v = edge[i].to, w = edge[i].w;
            if (dis[v] > dis[u] + w)
            {
                dis[v] = dis[u] + w;
                if (!inq[v])
                {
                    inq[v] = true;
                    //SLF优化
                    q.push_back(v);
                    if (++cnt[v] > n)  return false;
                    if (dis[q.back()] < dis[q.front()])
                    {
                        int k = q.back();
                        q.pop_back();
                        q.push_front(k);
                    }
                }
            }
        }
    }
    return true;
}

struct Edge2{
    int from, to, cap, flow;
    Edge2(int u, int v, int c, int f):from(u), to(v), cap(c), flow(f){}
};
struct EdmondsKarp{
    int n, m;
    vector<Edge2>edges;      //边数的两倍
    vector<int>G[maxn];     //邻接表,G[i][j]表示结点i的第j条边在e数组中的序号
    int a[maxn];            //当起点到i的可改进量
    int p[maxn];        //最短树上p的入弧编号

    void init(int n)
    {
        for(int i = 0;i <= n;i++)  G[i].clear();
        edges.clear();
    }

    void AddEdge(int from, int to, int cap)
    {
        edges.push_back(Edge2(from, to, cap, 0));
        edges.push_back(Edge2(to, from, 0, 0));
        m = edges.size();
        G[from].push_back(m-2);
        G[to].push_back(m-1);
    }

    ll Maxflow(int s, int t)
    {
        ll flow = 0;
        for(;;)
        {
            memset(a, 0, sizeof(a));
            queue<int>Q;
            Q.push(s);
            a[s] = INF2;
            while(!Q.empty())
            {
                int x = Q.front(); Q.pop();
                for(int i = 0;i < G[x].size();i++)
                {
                    Edge2& e = edges[G[x][i]];
                    if(!a[e.to] && e.cap > e.flow)
                    {
                        p[e.to] = G[x][i];
                        a[e.to] = min(a[x], e.cap-e.flow);
                        Q.push(e.to);
                    }
                }
                if(a[t])  break;
            }
            if(!a[t])  break;
            for(int u = t; u != s;u=edges[p[u]].from)
            {
                edges[p[u]].flow += a[t];
                edges[p[u]^1].flow -= a[t];
            }
            flow += a[t];
        }
        return flow;
    }
}ek;

int main()
{
    int T;
    scanf("%d", &T);
    while(T--)
    {
        init();
        scanf("%d%d", &n, &m);
        for(int i = 0;i < m;i++)
        {
            int u, v, w;
            scanf("%d%d%d", &u, &v, &w);
            addedge(u, v, w, id++);
        }
        SPFA(1);
        ek.init(n);
        for(int i = 1;i <= n;i++)
        {
            for(int j = head[i]; j != -1;j = edge[j].next)
            {
                int v = edge[j].to, w = edge[j].w;
                if(dis[v]-dis[i] == w)
                    ek.AddEdge(i, v, w);
            }
        }
        printf("%lld\n", ek.Maxflow(1, n));
    }
    return 0;
}

 

 

个性签名:时间会解决一切