UVA11478 Halum

translate

给定一个有向图,每条边都有一个权值,每次你可以选择一个节点v和一个整数d,把所有以v为终点的边的权值减小d,把所有以v为起点的边的权值增加d,最后要让所有边的最小值为正且尽量大

analysis

这是一道好题啊

首先可以分析出,我们选择节点v和整数d的先后顺序对我们的答案没有影响

既然这样的话,不妨对每个点 i i i记录一个值 x i x_i xi表示在这个点上的d的总和

这样一来,就可以用两点的x值来 O ( 1 ) O(1) O(1)的得到修改后的边权了,也就是:

w ( u , v ) = w 0 ( u , v ) + x u − x v w_(u,v)=w_0(u,v)+x_u-x_v w(u,v)=w0(u,v)+xuxv

然后发现这道题里面有一个特征:最小值为正且尽量大,也就是最小值最大,于是识别出二分来

进一步分析,发现假设使用模板:二分答案转化为判定,然后问题就转化为判定当前二分到的值是否合法

结合上面的式子,可以得到判断当前二分的值 a v e r aver aver是否合法,其实就仅仅是判断以下不等式组是否有解:

{ w 0 ( u 0 , v 0 ) + x u 0 − x v 0 > = a v e r w 0 ( u 1 , v 1 ) + x u 1 − x v 1 > = a v e r . . . . . . w 0 ( u m , v m ) + x u m − x v m > = a v e r \begin{cases} w_0(u_0,v_0)+x_{u_0}-x_{v_0}>=aver\\ w_0(u_1,v_1)+x_{u_1}-x_{v_1}>=aver\\ ......\\ w_0(u_m,v_m)+x_{u_m}-x_{v_m}>=aver\\ \end{cases} w0(u0,v0)+xu0xv0>=averw0(u1,v1)+xu1xv1>=aver......w0(um,vm)+xumxvm>=aver

显然,这就是差分约束了,因为把式子换一下就可以得到:

{ x u 0 − x v 0 > = a v e r − w 0 ( u 0 , v 0 ) x u 1 − x v 1 > = a v e r − w 0 ( u 1 , v 1 ) . . . . . . x u m − x v m > = a v e r − w 0 ( u m , v m ) \begin{cases} x_{u_0}-x_{v_0}>=aver-w_0(u_0,v_0)\\ x_{u_1}-x_{v_1}>=aver-w_0(u_1,v_1)\\ ......\\ x_{u_m}-x_{v_m}>=aver-w_0(u_m,v_m)\\ \end{cases} xu0xv0>=averw0(u0,v0)xu1xv1>=averw0(u1,v1)......xumxvm>=averw0(um,vm)

于是问题就转化为了:判断二分的值是否能够在使得上述不等式组中的每一个 x i x_i xi有一个正数解

于是就建好图然后跑spfa就可以了

注意队列要清空

(打了这道题后蒟蒻才发现自己原来打的spfa都是错的)

code

#include<bits/stdc++.h>
using namespace std;
#define loop(i,start,end) for(register int i=start;i<=end;++i)
#define clean(arry,num) memset(arry,num,sizeof(arry))
#define ll long long
int n,m;
const int maxn=510,maxm=3000;
struct node{
	int e;
	ll w;
	int nxt;
}edge[maxm<<2];
int cnt;
int head[maxn];
inline void change(ll w){
	loop(i,0,cnt-1)
		edge[i].w+=w;
}
inline void addl(int u,int v,ll w){
	edge[cnt].e=v;
	edge[cnt].w=w;
	edge[cnt].nxt=head[u];
	head[u]=cnt++;
}
ll dis[maxn];
int in[maxn];
bool vis[maxn];
queue<int>q;
ll L=1,R=-(LONG_LONG_MAX-10);
inline bool spfas(){
	clean(dis,0x3f);
	clean(vis,false);
	clean(in,0);
	while(q.empty()==false)
		q.pop();
	dis[0]=0;
	vis[0]=true;
	++in[0];
	q.push(0);
	while(q.empty()==false){
		int f=q.front();
		q.pop();
		vis[f]=false;
		for(int i=head[f];i!=-1;i=edge[i].nxt){
			int v=edge[i].e;
			if(dis[v]>dis[f]+edge[i].w){
				dis[v]=dis[f]+edge[i].w;
				if(!vis[v]){
					if(++in[v]>n)
						return false;
					q.push(v);
					vis[v]=true;
				}
			}
		}
	}
	return true;
}
inline bool spfa(ll mid){
	change(-mid);
	bool res=spfas();
	change(mid);
	return res;
}
void bin(){
	if(spfa(R)){
		printf("Infinite\n");
		return;
	}
	else if(!spfa(L)){
		printf("No Solution\n");
		return;
	}
	ll l=L,r=R;
	ll res=L;
	while(l<r){
		ll mid=(l+r)>>1;
		if(spfa(mid)){
			res=mid;
			l=mid+1;
		}
		else
			r=mid;
	}
	printf("%lld\n",res);
}
int main(){
	while(scanf("%d%d",&n,&m)!=EOF){
		int ui,vi,di;
		cnt=0;
		clean(head,-1);
		loop(i,1,m){
			scanf("%d%d%d",&ui,&vi,&di);
			addl(ui,vi,(ll)di);
			R=max(R,(ll)di);
		}
		loop(i,1,n)
			addl(0,i,0);
		bin();
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

AndrewMe8211

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值