题意
数据范围
对于 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
和不连通,经典最小割问题,由于一种边有两种割法,所以要拆点。在原边的中间新建一个点来增加一条边。然后第二问答案就求出来了?第一问实际上是让我们判断最小割的唯一性,考虑对残量网络进行
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;
}