题目:Halum
题意:给出一张带权的有向图(可能有负权),可以对任意一个点进行操作,选择一个整数d,使这个点的所有出边上的值增加d,入边上的值减少d,问可能出现的最大的最小边权(要求这个值为正整数)。
思路:
假设在同一个点上操作了两次,选的数是d1和d2,那么第一次把所有出边上的值增加d1,所有入边上的值减少d1,第二次把所有出边上的值增加d2,所有入边上的值减少d2。此时从整体上看,每条入边上的值共减少d1+d2,出边上的值增加d1+d2,也就相当于对这个点选择了d1+d2进行了一次操作。所以为了方便起见,可以不妨设对每个点只操作了一次(不操作相当于d=0)。此时,假设在第i个点上加了sum[i]。
二分最大的最小边权,设为p。对于每一条边x->y,改动后的边权w[x][y]+sum[x]-sum[y]>p,变形之后得sum[x]-sum[y]>p-w[x][y],即sum[y]-sum[x]<w[x][y]-p。此时就形成了一个每个不等式形如x-y<a的差分约束系统。
所以只用在原图中把每条边的权减去p,在图中用spfa判负权回路。如果存在负权回路,那么差分约束问题无解,当前的p比答案大;否则差分约束问题有解,当前的p小于等于答案。
如果p等于最大的边权值加1,差分约束系统仍有解,原问题的结为INF。如果p等于1时仍存在负权回路,那么原问题无解。
代码:
#include<bits/stdc++.h>
using namespace std;
#define maxn 500
#define maxm 2700
struct Pair {
int x,y;
Pair(int xx=0,int yy=0) {
x=xx,y=yy;
}
};
int n,m;
vector<Pair> a[maxn+5];
void init() {
for(int i=1; i<=n; i++) a[i].clear();
}
int readin(int& r) {
for(int i=1; i<=m; i++) {
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
a[x].push_back(Pair(y,z));
r=max(z,r); //此处写错写成 r=max(y,r) QwQ……
}
}
bool spfa() {
queue<int> que;
int cnt[maxn+5]= {0};
int dist[maxn+5]= {0};
bool u[maxn+5]= {0};
for(int i=1; i<=n; i++) {
que.push(i);
dist[i]=0,u[i]=true;
}
while(!que.empty()) {
int h=que.front();
que.pop();
u[h]=false;
for(int i=0; i<a[h].size(); i++) {
int x=a[h][i].x;
int y=a[h][i].y;
if(dist[x]>dist[h]+y) {
dist[x]=dist[h]+y;
if(!u[x]) {
u[x]=true,que.push(x);
if(++cnt[x]>n) return false;
}
}
}
}
return true;
}
bool judge(int x) {
for(int i=1; i<=n; i++) {
for(int j=0; j<a[i].size(); j++) {
a[i][j].y-=x;
}
}
int ans=spfa();
for(int i=1; i<=n; i++) {
for(int j=0; j<a[i].size(); j++) {
a[i][j].y+=x;
}
}
return ans;
}
int binsearch(int l,int r) {
while(r-l>1) {
int mid=l+(r-l)/2;
if(judge(mid)) l=mid;
else r=mid;
}
if(judge(l+1)) l++;
if(!judge(l)) l--;
return l;
}
int main() {
while(~scanf("%d%d",&n,&m)) {
init();
int l=2,r=-10000;
readin(r);
if(judge(r+1)) {
printf("Infinite\n");
continue;
}
if(!judge(1)) {
printf("No Solution\n");
continue;
}
int ans=binsearch(l,r);
printf("%d\n",ans);
}
return 0;
}