现在假设受众已有 求图的所有最短路径 的前置知识

推荐阅读:白书P209 看懂最大流&最小割

今天没时间重构代码了。。。

就随便注释一下

题目呢是签到题

意思是给一个有向图

一个人要从1点走到n点

我们要阻碍他走最短路

而你堵塞一条边的代价就是这条边的长度

问最小代价

要是看懂了最小割
就知道这题几乎就是板子题

先用优先队列优化最短路,求所有最短路径的所有边
题解的方法可以学一手

采用的方法是先更新好所有点的最短距离 (题中的sp数组)
然后遍历所有边
接着如果说两个点的最短距离之差刚好是边的长度
那么这条边就是在最短路上

就是题解那个蜜汁公式的意思

for(int pos=1;pos<=n;++pos)
    for(auto &e : D::G[pos])
        if(sp[e.to]-sp[pos]==e.v)
            I::AddEdge(pos,e.to,e.v);

上图的下面的for等价于

for(int i=0;i<D::G[pos].size();i++){
    D::Edge e=D::G[pos][i];
}

学一手auto自适应类型

然后用最大流求答案。。。。

似乎就没了

#include 

template 
bool Reduce(T &a,T const &b) {
    return a>b?a=b,1:0;
}

const int XN=1e4+11;
const int INF=2e9;
const long long oo=1e18;

int n;
long long sp[XN];

namespace D {
    struct Edge {
        int to,v;
    };

    std::vector G[XN];
    //优先队列优化
    void Run() {
        static bool ud[XN];
        std::priority_queue,std::vector >,std::greater > > Q;//小根堆
        std::fill(sp+1,sp+1+n,oo);
        std::fill(ud+1,ud+1+n,0);
        sp[1]=0;
        Q.push(std::make_pair(0,1));
        while(!Q.empty()) {
            int pos=Q.top().second;Q.pop();
            if(ud[pos])
                continue;
            ud[pos]=1;
            for(auto int & e : G[pos]) {
                int u=e.to;
                if(Reduce(sp[u],sp[pos]+e.v))
                    Q.push(std::make_pair(sp[u],u));
            }
        }
    }
}

namespace I {
    //最大流求解
    struct Edge {
        int to,cap,v;
        Edge *rev,*pre;
    }*G[XN],*preArc[XN];

    void AddEdge(int x,int y,int c) {
        G[x]=new Edge{y,c,0,NULL,G[x]};
        G[y]=new Edge{x,0,0,G[x],G[y]};
        G[x]->rev=G[y];
    }

    int Aug() {
        int d=INF;
        for(int pos=n;preArc[pos];pos=preArc[pos]->rev->to)
            Reduce(d,preArc[pos]->cap-preArc[pos]->v);
        for(int pos=n;preArc[pos];pos=preArc[pos]->rev->to) {
            preArc[pos]->v+=d;
            preArc[pos]->rev->v-=d;
        }
        return d;
    }

    long long Run() {
        static int num[XN],d[XN];
        static Edge *cArc[XN];
        std::fill(num+1,num+n,0);
        std::fill(d+1,d+1+n,0);
        std::copy(G+1,G+1+n,cArc+1);
        num[0]=n;preArc[1]=0;
        long long flow=0;
        for(int pos=1;d[1]<n;) {
            if(pos==n) {
                flow+=Aug();
                pos=1;
            }
            bool adv=0;
            for(Edge *&e=cArc[pos];e;e=e->pre) {
                int u=e->to;
                if(e->cap>e->v && d[u]+1==d[pos]) {
                    adv=1;
                    preArc[pos=u]=e;
                    break;
                }
            }
            if(!adv) {
                if(--num[d[pos]]==0)
                    break;
                d[pos]=n;
                for(Edge *e=cArc[pos]=G[pos];e;e=e->pre)
                    if(e->cap>e->v)
                        Reduce(d[pos],d[e->to]+1);
                num[d[pos]]++;
                if(pos!=1)
                    pos=preArc[pos]->rev->to;//cArc
            }
        }
        return flow;
    }
}

int main() {
    //freopen("test1.in","r",stdin);
    std::ios::sync_with_stdio(0);
    std::cin.tie(0);
    int T;std::cin>>T;
    while(T--) {
        int m;std::cin>>n>>m;
        for(int i=1;i<=n;++i) {
            D::G[i].clear();
            I::G[i]=0;
        }
        for(int i=1;i<=m;++i) {
            int x,y,v;
            std::cin>>x>>y>>v;
            D::G[x].push_back({y,v});
        }

        D::Run();

        if(sp[n]==oo)
            std::cout<<0<<'\n';
        else {
            //蜜汁公式 求出所有最短路径边
            for(int pos=1;pos<=n;++pos)
                for(auto &e : D::G[pos])
                    if(sp[e.to]-sp[pos]==e.v)
                        I::AddEdge(pos,e.to,e.v);

            std::cout<<I::Run()<<'\n';
        }
    }
    return 0;
}