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)+xu−xv
然后发现这道题里面有一个特征:最小值为正且尽量大,也就是最小值最大,于是识别出二分来
进一步分析,发现假设使用模板:二分答案转化为判定,然后问题就转化为判定当前二分到的值是否合法
结合上面的式子,可以得到判断当前二分的值 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)+xu0−xv0>=averw0(u1,v1)+xu1−xv1>=aver......w0(um,vm)+xum−xvm>=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} ⎩⎪⎪⎪⎨⎪⎪⎪⎧xu0−xv0>=aver−w0(u0,v0)xu1−xv1>=aver−w0(u1,v1)......xum−xvm>=aver−w0(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;
}