JZOJ3348. 【NOI2013模拟】秘密任务

2 篇文章 0 订阅
1 篇文章 0 订阅

题意

数据范围

对于 10% 的数据: 2 ≤ N ≤ 10 , 1 ≤ M ≤ 20。
另有 40% 的数据: 最优 方案 是唯一 的。
对于 10 0% 的数据:2 ≤ N ≤ 400,1 ≤ M ≤ 4000,1 ≤ T ≤ 5,1 ≤ Ai, c ≤ 10^9。无向图可能有重边。

Analysis

首先观察题意,是在所有最短路上搞事情,先建出最短路图,点比较少直接朴素 Dij D i j 。题目实质上是让我们删去一些边,使 1 1 N不连通,经典最小割问题,由于一种边有两种割法,所以要拆点。在原边的中间新建一个点来增加一条边。然后第二问答案就求出来了?第一问实际上是让我们判断最小割的唯一性,考虑对残量网络进行 Tarjan T a r j a n 缩点。
若一条边能成为割边则它满足该边满流,且它两边的点所属 SCC S C C 不同(如果相同,代表还有边没割断)。一条边必定成为割边的条件,设边两边的点为 x,y x , y
SCC[x]=SCC[1] S C C [ x ] = S C C [ 1 ] && SCC[y]==SCC[n] S C C [ y ] == S C C [ n ] ,若一条割边不满足,则它有可能成为割边,也就是最小割不具有唯一性,这道题就解决了。

Code

# include<cstdio>
# include<cstring>
# include<algorithm>
using namespace std;
const int N = 800 + 5;
const int M = 4e3 + 5;
const int inf = 0x3f3f3f3f;
typedef long long ll;
ll dis[2][N];
int vis[N],val[N],dep[N],q[N],dfn[N],low[N],st1[N],to1[M << 1],nx1[M << 1],len[M << 1];
int to[M << 1],nx[M << 1],st[N],flow[M << 1],sta[N],bel[N];
int n,m,tot,t,cnt,top,siz,mx;
ll ans;
void add(int u,int v,int w)
{
    to[++tot] = v,nx[tot] = st[u],st[u] = tot,flow[tot] = w;
    to[++tot] = u,nx[tot] = st[v],st[v] = tot,flow[tot] = 0;
}
void add1(int u,int v,int w)
{
    to1[++tot] = v,nx1[tot] = st1[u],st1[u] = tot,len[tot] = w;
    to1[++tot] = u,nx1[tot] = st1[v],st1[v] = tot,len[tot] = w;
}
inline bool bfs()
{
    memset(dep,0xff,sizeof(dep));
    int h = 1,t = 0; q[++t] = 1,dep[1] = 0;
    while (h <= t)
    {
        int x = q[h++];
        for (int i = st[x] ; i ; i = nx[i])
        {
            if (!flow[i] || ~dep[to[i]]) continue;
            dep[to[i]] = dep[x] + 1,q[++t] = to[i];
        }
    }
    return dep[n] != -1;
}
inline int solve(int x,int mi)
{
    if (x == n) return mi;
    int ret = 0,now = 0;
    for (int i = st[x] ; i ; i = nx[i])
        if (dep[to[i]] == dep[x] + 1 && flow[i] && (now = solve(to[i],min(mi,flow[i]))))
        {
            ret += now,flow[i ^ 1] += now;
            flow[i] -= now,mi -= now; if (!mi) break;
        }
    if (!ret) dep[x] = -1;
    return ret;
}
inline void tarjan(int x)
{
    sta[++top] = x,dfn[x] = low[x] = ++mx;
    for (int i = st[x] ; i ; i = nx[i])
    {
        if (!flow[i]) continue;
        if (!dfn[to[i]])
        {
            tarjan(to[i]);
            low[x] = min(low[x],low[to[i]]);
        }else if (!bel[to[i]]) low[x] = min(low[x],dfn[to[i]]);
    }
    if (low[x] == dfn[x])
    {
        ++siz;
        while (sta[top] != x) bel[sta[top--]] = siz;
        bel[sta[top--]] = siz;
    }
}
int main()
{
    scanf("%d",&t);
    while (t--)
    {
        memset(st,0,sizeof(st)),memset(st1,0,sizeof(st1));
        memset(dfn,0,sizeof(dfn)),memset(low,0,sizeof(low));
        mx = top = siz = ans = tot = 0;
        scanf("%d%d",&n,&m);
        for (int i = 1 ; i < n ; ++i) scanf("%d",&val[i]);
        val[n] = inf;
        for (int i = 1 ; i <= m ; ++i)
        {
            int u,v,w;
            scanf("%d%d%d",&u,&v,&w); add1(u,v,w);
        } tot = 1;
        memset(vis,0,sizeof(vis)),memset(dis[0],0x7f,sizeof(dis[0]));
        dis[0][1] = 0;
        for (int i = 1 ; i <= n ; ++i)
        {
            int mi = 0;
            for (int j = 1 ; j <= n ; ++j)
                if (dis[0][j] < dis[0][mi] && !vis[j]) mi = j;
            vis[mi] = 1;
            for (int j = st1[mi] ; j ; j = nx1[j])
                if (dis[0][to1[j]] > dis[0][mi] + len[j])
                    dis[0][to1[j]] = dis[0][mi] + len[j];
        }
        memset(vis,0,sizeof(vis)),memset(dis[1],0x7f,sizeof(dis[1]));
        dis[1][n] = 0;
        for (int i = 1 ; i <= n ; ++i)
        {
            int mi = 0;
            for (int j = 1 ; j <= n ; ++j)
                if (dis[1][j] < dis[1][mi] && !vis[j]) mi = j;
            vis[mi] = 1;
            for (int j = st1[mi] ; j ; j = nx1[j])
                if (dis[1][to1[j]] > dis[1][mi] + len[j])
                    dis[1][to1[j]] = dis[1][mi] + len[j];
        }
        memset(bel,0,sizeof(bel)); cnt = n;
        for (int i = 1 ; i < n ; ++i)
            for (int j = st1[i] ; j ; j = nx1[j])
                if (dis[0][i] + dis[1][to1[j]] + len[j] == dis[0][n] && i != j)
                {
                    ++cnt; add(i,cnt,val[i]);
                    add(cnt,to1[j],val[to1[j]]);
                }
        while (bfs()) ans += solve(1,inf);
        for (int i = 1 ; i <= cnt ; ++i) if (!dfn[i]) tarjan(i);
        bool flag = 1;
        for (int i = 2 ; i <= tot ; i += 2)
        {
            if (flow[i] || bel[to[i ^ 1]] == bel[to[i]]) continue;
            if (bel[to[i]] != bel[n] || bel[to[i ^ 1]] != bel[1]) { flag = 0; break; }
        }
        if (flag) printf("Yes ");
        else printf("No ");
        printf("%lld\n",ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值