【NOI(P)2013模拟】秘密任务

10 篇文章 0 订阅
4 篇文章 1 订阅

Description

这里写图片描述

分析

这题是在最短路上研究问题的,所以理所当然要建个图先。
其次我们先考虑费用,看看这图,再YY一下,那么我们可以发现这就是典型的最小割问题,可是我们还是要考虑如何判断这个割是否唯一。
首先,大家可以先去看我的另一个blog,上面有写类似这样的判断最大流是否唯一。http://blog.csdn.net/xieguofu2014/article/details/50682700
其实这两题是不同的。。。所以我在考试时就挂了。。
因为这题比较特殊,不是一般的网络流(这个自己体会一下吧。。不好说。)
但是我们知道也有一些条件是一定要满足的比如说
某些边流满的边且左右两边属于s和t集的原流量和一定是原最大流的值,即所有属于割集的边的流量和是否为原最大流,这是来预防两条连续满流的边
另一个条件是一条一定是割集的边一条边的两个节点的费用一样,那么这也是有多解的

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=405;
const int M=4005;
char cher[10],chee[10];
ll x[M],y[M],e[M*2],er[M*2],n,m,z[M],val[N],nu,b[M*2],las[N],nex[M*2],dis[N],l,r,a[M*2],v[M*2],tot[N],re[M*2],t,ty[M][3],q[N][N];
bool bz[N];
void add(int x,int y,int z,int k){
    b[++nu]=y;e[nu]=x;nex[nu]=las[x];las[x]=nu;v[nu]=z;re[nu]=nu+1;er[nu]=z;
    if (k) b[++nu]=x,nex[nu]=las[y],las[y]=nu,v[nu]=0,re[nu]=nu-1;
}
bool pd(){
    int l=0,r=1;a[1]=1;memset(tot,0,sizeof(tot));tot[1]=1;
    while (l<r){
        for(int p=las[a[++l]];p;p=nex[p])
        if (v[p]>0&&!tot[b[p]]) a[++r]=b[p],tot[b[p]]=tot[a[l]]+1;
    }
    return tot[n];
}
int ditch(int x,ll y){
    if (x==n) return y;
    ll we=0;
    for(int p=las[x];p;p=nex[p]){
        if (tot[b[p]]==tot[x]+1&&v[p]>0){
            int w=ditch(b[p],min(y,v[p]));
            if (w) {
                y=y-w;v[p]=v[p]-w;v[re[p]]=v[re[p]]+w;
                we=we+w;if (!y) break;
            }
        }
    }
    return we;
}
int bed[N];
void dfs(int x,int z){
    bed[x]=z+1;
    for(int p=las[x];p;p=nex[p])
    if (v[p^z]&&!bed[b[p]]) dfs(b[p],z);
}
int main(){
    scanf("%lld",&t);
    for(;t;t--){
        scanf("%lld %lld",&n,&m);nu=0;
        memset(las,0,sizeof(las));
        for(int i=1;i<=n-1;i++) scanf("%lld",&val[i]);
        for(int i=1;i<=m;i++){
            scanf("%lld %lld %lld",&x[i],&y[i],&z[i]);
            add(x[i],y[i],z[i],0),add(y[i],x[i],z[i],0);
        }
        memset(dis,127,sizeof(dis));
        memset(bz,0,sizeof(bz));
        l=0;r=1;a[1]=1;bz[1]=1;dis[1]=0;
        while (l<r){
            for(int p=las[a[++l]];p;p=nex[p]){
                if (dis[b[p]]>dis[a[l]]+v[p]) {
                    dis[b[p]]=dis[a[l]]+v[p];
                    if (!bz[b[p]])bz[b[p]]=1,a[++r]=b[p];
                }
            }bz[a[l]]=0;
        }
        memset(las,0,sizeof(las));val[n]=2000000007;
        int cnt=0;nu=1;
        for(int i=1;i<=m;i++){
            if (dis[x[i]]==dis[y[i]]+z[i]) add(y[i],x[i],min(val[x[i]],val[y[i]]),1);
            if (dis[y[i]]==dis[x[i]]+z[i]) add(x[i],y[i],min(val[x[i]],val[y[i]]),1);
        }
        ll ans=0;
        while (pd()) ans+=ditch(1,4005000000000);
        memset(bed,0,sizeof(bed));
        dfs(1,0);
        dfs(n,1);
        ll sum=0,pew=0;
        for(int i=1;i<=nu;i++)
        if ((!(i&1))&&(bed[b[i]]==2&&bed[e[i]]==1)&&!v[i]) {
            sum+=er[i];if (val[b[i]]==val[e[i]]) pew=1;
        }
        cher[0]='Y',cher[1]='e',cher[2]='s';chee[0]='N';chee[1]='o';
        if (sum!=ans) {
            printf("%s %d\n",chee,ans);continue;
        }
        if (pew) printf("%s %lld\n",chee,ans);else printf("%s %lld\n",cher,ans);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值