JZOJ 3348. 【NOI2013模拟】秘密任务【网络流】【最小割】【最短路】

3 篇文章 0 订阅
2 篇文章 0 订阅


题目:

传送门


题意:

有一个反动派想通过各个城市中的高速公路走最短路前往 N N N城,而我们需要在部分城市的入口或出口建立拦路点,但每个城市建立的费用是不一样的。我们需要求出最小的费用使得反动派无法走到 N N N


分析:

因为要在最短路上跑,所以我们直接在原图上跑一遍最短路,随后再在跑出来的最短路上建立网络流。
对于唯一性的判断,我们可以根据最小割的一条性质进行处理:

若一条边为(u,v),而u与S相连,v与T相连,则这条边必定为最小割的其中之一,且方案不唯一

代码:

#pragma GCC optimize("Ofast")
#pragma GCC optimize("inline")
#include<cstdio>
#include<string>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#define LL long long 
#define LZX ILU
using namespace std;
inline LL read() {
    LL d=0,f=1;char s=getchar();
    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9'){d=d*10+s-'0';s=getchar();}
    return d*f;
}  
using namespace std;
struct node{
	LL to,next,w;
}e[8005];
LL cnt,ls[8005],a[8005],tf[8005],n,m;
void add(LL x,LL y,LL w)
{
	e[++cnt]=(node){y,ls[x],w};
	ls[x]=cnt;
	return;
}
struct dui{
	LL num,w;
	bool operator < (const dui &www) const {return w>www.w;}
};
LL ans[100005];
void dij(LL s,LL *ans)
{
	for(LL i=1;i<=n;i++) ans[i]=(LL)1e18;
	ans[s]=0; 
	priority_queue<dui> q;
	q.push((dui){s,0});
	while(q.size())
	{
		LL u=q.top().num,v=q.top().w;
		q.pop();
		if(v!=ans[u]) continue;
		for(LL i=ls[u];i;i=e[i].next)
		  if(v+e[i].w<ans[e[i].to])
		    ans[e[i].to]=v+e[i].w,q.push((dui){e[i].to,ans[e[i].to]});
	}
	return;
}
LL dis[8005],dis2[8005],p[405][405];
struct wifi{
	LL to,next,w;
}net[8005];
LL le[8005],tot,S,T,tt;
void addl(LL x,LL y,LL w)
{
	net[++tot]=(wifi){y,le[x],w};
	le[x]=tot;
	net[++tot]=(wifi){x,le[y],0};
	le[y]=tot;
	return;
}
LL co[(LL)1e6],dt[(LL)1e6],cur[(LL)1e6];
LL dfs(LL x,LL flow)
{
	if(x==T) return flow;
	LL use=0;
	for(LL i=cur[x];i;i=net[i].next,cur[x]=i)
	{
		LL v=net[i].to;
		if(net[i].w&&dt[v]+1==dt[x])
		{
			LL tmp=dfs(v,min(flow-use,net[i].w));
			net[i].w-=tmp;net[i^1].w+=tmp;use+=tmp;
			if(use==flow) return use;
		}
	}
	cur[x]=le[x];
	if(!(--co[dt[x]])) dt[S]=tt;
	++co[++dt[x]];
	return use;
}
void dg(LL x)
{
	if(tf[x]) return;
	tf[x]=1;
	for(LL i=le[x];i;i=net[i].next) if(net[i].w) dg(net[i].to);
	return; 
}
void dg2(LL x)
{
	if(tf[x]) return;
	tf[x]=2;
	for(LL i=le[x];i;i=net[i].next) if(net[i^1].w) dg2(net[i].to);
	return;
}
int main()
{
	LL q=read();
	while(q--)
	{
		cnt=0;memset(ls,0,sizeof(ls));memset(e,0,sizeof(e));
		n=read(),m=read();
		for(LL i=1;i<n;i++) a[i]=read();
		a[n]=1e18;
		for(LL i=1;i<=m;i++) 
		{
			LL x=read(),y=read(),w=read();
			add(x,y,w);add(y,x,w);
		}
		memset(tf,0,sizeof(tf));
		dij(1,dis);dij(n,dis2);
		memset(tf,0,sizeof(tf));	
		for(LL i=1;i<=n;i++) tf[i]=(dis[i]+dis2[i]==dis[n]);
		memset(p,0,sizeof(p));
		for(LL i=1;i<=n;i++)
		{
			if(!tf[i]) continue;
			for(LL j=ls[i];j;j=e[j].next)
			  if(dis[i]+e[j].w==dis[e[j].to]&&tf[e[j].to]) p[i][e[j].to]++;
		}
		for(int i=1;i<=tt;i++) le[i]=dt[i]=co[i]=cur[i]=0;
        for(int i=2;i<=cnt;i++) e[i].next=0;
        tot=1;S=1;T=tt=n;
		for(LL i=1;i<=n;i++)
		  for(LL j=1;j<=n;j++)
		  {
		  	if(!p[i][j]) continue;
		  	tt++;
		  	addl(i,tt,(LL)p[i][j]*a[i]);addl(tt,j,(LL)p[i][j]*a[j]);
		  }
		co[0]=tt;LL ans=0;
		for(;dt[S]<tt;) ans+=dfs(S,1LL<<62);
		memset(tf,0,sizeof(tf));
		dg(S);
		dg2(T);
		LL ans2=0;
		for(LL i=1;i<=tt;i++) 
		  for(LL j=le[i];j;j=net[j].next)
		    if(tf[i]&&tf[net[j].to]&&tf[i]!=tf[net[j].to]) 
				ans2+=net[j].w;
		if(ans==ans2) 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、付费专栏及课程。

余额充值