H y p e r l i n k Hyperlink Hyperlink
https://www.lydsy.com/JudgeOnline/problem.php?id=3258
D e s c r i p t i o n Description Description
给定一张 n n n点 m m m边无向图,现有一个 s b sb sb要从点1走到点 n n n,这个 s b sb sb只走最短路,在某个点设置站点可以废掉这个点的一条边,但需要耗费一定代价,现在要让这个 s b sb sb无论怎么走都走不到终点,求最小设立的站点总代价以及方案是否唯一
n ≤ 400 , m ≤ 4000 n\leq 400,m\leq 4000 n≤400,m≤4000
S o l u t i o n Solution Solution
首先因为这个 s b sb sb直走最短路,所以我们将原图的最短路会经过的边处理出来,这样图就变成了一张有向无环图(由于笔者考试时忘记这个算法咋写了,导致没拿分2333)
具体流程:
- 从点1和点 n n n分别出发跑一遍最短路,这两点与其他点的距离分别记为 m i n s i mins_i minsi和 m i n t i mint_i minti
- 枚举每条边,若这条边 ( u , v , w ) (u,v,w) (u,v,w)是其中一条点1到点 n n n最短路的必经边,当且仅当 m i n s u + m i n t v + w = m i n s n mins_u+mint_v+w=mins_n minsu+mintv+w=minsn
随后变成了有向无环图后,问题变成了:在这张图中切断若干条边,使得点1和点 n n n不连通得同时价值最小
把点1看做源点,点 n n n看做汇点,这 t m tm tm不就是一道裸的最小割吗?(考试时就想到了2333)
好了到这里你就愉快的有40分了,但如何判断方案唯一呢?
从点1和点 n n n遍历一遍剩余流,判断一下即可(据说还有大佬用 T a r j a n Tarjan Tarjan%%%)
时间复杂度: O ( n 2 m ) O(n^2m) O(n2m)【 x j q xjq xjq说这是 d i n i c dinic dinic的复杂度,但不知道为啥题解上写 O ( n m 2 ) O(nm^2) O(nm2)】
C o d e Code Code
#include<queue>
#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
#define LL long long
#define N 410
#define M 4010
using namespace std;
int n,m,a[N];
LL mins[N],mint[N],ans;
inline LL read()
{
char c;int f=0,d=1;
while(c=getchar(),!isdigit(c)) if(c=='-') d=-1;f=(f<<3)+(f<<1)+c-48;
while(c=getchar(),isdigit(c)) f=(f<<3)+(f<<1)+c-48;
return d*f;
}
struct Spfa
{
int l[N],tot;
struct node{int from,next,to,w;}e[M<<1];
inline void add(int u,int v,int w)//建边
{
e[++tot]=(node){u,l[u],v,w};l[u]=tot;
e[++tot]=(node){v,l[v],u,w};l[v]=tot;
return;
}
inline void run(int S,LL *dis)//Spfa
{
queue<int>q;
bool vis[N]={0};for(register int i=1;i<=n;i++) dis[i]=1LL<<50LL;
q.push(S);vis[S]=true;dis[S]=0;
while(q.size())
{
int x=q.front();q.pop();vis[x]=true;
for(register int i=l[x];i;i=e[i].next)
{
int y=e[i].to,w=e[i].w;
if(dis[x]+w<dis[y])
{
dis[y]=dis[x]+w;
if(!vis[y]) vis[y]=true,q.push(y);
}
}
vis[x]=false;
}
return;
}
}A;
struct Dinic
{
int l[N],tot,d[N],s,t;
struct node{int next,to,w;}e[M<<1];
inline void add(int u,int v,int w)
{
e[tot]=(node){l[u],v,w};l[u]=tot++;
e[tot]=(node){l[v],u,0};l[v]=tot++;
return;
}
inline bool bfs()
{
memset(d,-1,sizeof(d));
queue<int>q;d[s]=0;q.push(s);
while(q.size())
{
int x=q.front();q.pop();
for(int i=l[x];~i;i=e[i].next)
{
int y=e[i].to;
if(e[i].w&&d[y]==-1)
{
d[y]=d[x]+1;
if(y==t) return true;
q.push(y);
}
}
}
return false;
}
inline int dfs(int x,int flow)
{
if(x==t||!flow) return flow;
int rest=0,f=0;
for(int i=l[x];~i;i=e[i].next)
{
int y=e[i].to;
if(d[x]+1==d[y]&&e[i].w)
{
f=dfs(y,min(flow-rest,e[i].w));
if(!f) d[y]=-1;
e[i].w-=f;rest+=f;e[i^1].w+=f;
}
}
if(!rest) d[x]=-1;
return rest;
}
inline LL dinic()
{
LL res=0;
while(bfs()) res+=dfs(s,1<<30);
return res;
}
}B;
int vis[N];
inline int wei(int u,int v){return v==n?a[u]:min(a[u],a[v]);}
inline void dfs1(int x)
{
vis[x]=1;
for(register int i=B.l[x];~i;i=B.e[i].next)
{
int y=B.e[i].to;
if(!vis[y]&&B.e[i].w) dfs1(y);
}
return;
}
inline void dfs2(int x)
{
vis[x]=2;
for(register int i=B.l[x];~i;i=B.e[i].next)
{
int y=B.e[i].to;
if(!vis[y]&&B.e[i^1].w) dfs2(y);
}
return;
}
inline bool special()
{
LL sum=0;
for(register int i=1;i<n;i++)
if(vis[i]==1)
for(register int j=B.l[i],y;~j;j=B.e[j].next)
if(vis[y=B.e[j].to]==2&&!(j&1))
{
if(a[i]==a[y]) return 0;
sum+=wei(i,y);
}
return sum==ans;
}
signed main()
{
for(int t=read();t--;)
{
A.tot=B.tot=0;
memset(A.l,0,sizeof(A.l));
memset(B.l,-1,sizeof(B.l));
n=read();m=read();B.s=1;B.t=n;
for(register int i=1;i<n;i++) a[i]=read();
for(register int i=1,u,v,w;i<=m;i++)
{
u=read();v=read();w=read();
A.add(u,v,w);
}
A.run(1,mins);A.run(n,mint);
for(register int i=1;i<=A.tot;i++)
{
int u=A.e[i].from,v=A.e[i].to,w=A.e[i].w;
if(mins[u]+mint[v]+w>mins[n]) continue;
B.add(u,v,wei(u,v));
}
ans=B.dinic();
memset(vis,0,sizeof(vis));
dfs1(1);dfs2(n);
if(special()) printf("Yes ");
else printf("No ");
printf("%lld\n",ans);
}
}