[CF1450E]Capitalism
壹、题目描述 ¶
贰、题解 ¶
这个输入真的很......差分约束。
回忆差分约束的细节,构造形如 \(x\le y+w\),然后从 \(y\) 向 \(x\) 连权为 \(w\) 的边。
如果 \(b=1\),由于 \(a_j=a_i+1\),我们可以得到 \(a_j\ge a_i+1\;\and\;a_j\le a_i+1\),所以加入边 \(\lang i,j,1\rang\) 与 \(\lang j,i,-1\rang\).
如果 \(b=0\),由于 \(|a_i-a_j|=1\),所以我们可以得到 \(-1\le a_i-a_j\le 1\Rightarrow a_i\ge a_j-1\;\and\;a_i\le a_j+1\),然后我们可以加入边 \(\lang i,j,1\rang\) 与 \(\lang j,i,1\rang\).
建立图之后,跑最短路,由于有负权边,所以用 \(\rm SPFA\) 或者 \(\rm floyd\),对于跑出来的结果,如果有负环就 \(\rm GG\),否则我们尝试构造。
我们枚举起点,找到其构成的极差,并判断存在边的两点是否最短路差值为 \(1\),如果为 \(0\),那么这个起点一定不合法。
在所有的极差中选择最大的一个作为最终起点,特别地,如果没有起点合法,答案也无解。
FAQ
\(\mathcal Q_1:\) 连边意义是什么?
\(\mathcal A_1:\) 从 \(u\) 向 \(v\) 连一条 \(w\) 的边,即保证 \(v\le u+w\),否则 \(v\) 就会被 \(u+w\) 更新。
\(\mathcal Q_2:\) 为什么有负环就 \(\rm GG\)?
\(\mathcal A_2:\) 如果在 \(u\) 出现负环,那么就表示存在一个环,它要求自己仰慕自己。
\(\mathcal Q_3:\) 为什么存在边的两点是否最短路差值为 \(0\) 无解呢?
\(\mathcal A_3:\) 假设起点为 \(s\),这两点为 \(a,b\),那么就有一个包含 \(s,a,b\) 的奇环,又因为相邻边差值为 \(1\),通过奇偶性可以判断,这一定是无解的。
叁、参考代码 ¶
#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
using namespace std;
// #define NDEBUG
#include<cassert>
namespace Elaina{
#define rep(i, l, r) for(int i=(l), i##_end_=(r); i<=i##_end_; ++i)
#define drep(i, l, r) for(int i=(l), i##_end_=(r); i>=i##_end_; --i)
#define fi first
#define se second
#define mp(a, b) make_pair(a, b)
#define Endl putchar('\n')
#define mmset(a, b) memset(a, b, sizeof a)
// #define int long long
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
template<class T>inline T fab(T x){ return x<0? -x: x; }
template<class T>inline void getmin(T& x, const T rhs){ x=min(x, rhs); }
template<class T>inline void getmax(T& x, const T rhs){ x=max(x, rhs); }
template<class T>inline T readin(T x){
x=0; int f=0; char c;
while((c=getchar())<'0' || '9'<c) if(c=='-') f=1;
for(x=(c^48); '0'<=(c=getchar()) && c<='9'; x=(x<<1)+(x<<3)+(c^48));
return f? -x: x;
}
template<class T>inline void writc(T x, char s='\n'){
static int fwri_sta[1005], fwri_ed=0;
if(x<0) putchar('-'), x=-x;
do fwri_sta[++fwri_ed]=x%10, x/=10; while(x);
while(putchar(fwri_sta[fwri_ed--]^48), fwri_ed);
putchar(s);
}
}
using namespace Elaina;
const int maxn=200;
const int inf=0x3f3f3f3f;
vector<pii>e;
int f[maxn+5][maxn+5];
int n, m;
inline void add(int x, int y, int w){
// printf("add:> %d %d %d\n", x, y, w);
f[x][y]=w;
}
inline void input(){
n=readin(1), m=readin(1);
memset(f, 0x3f, sizeof f);
int x, y, d;
rep(i, 1, m){
x=readin(1), y=readin(1), d=readin(1);
if(d==1) add(x, y, 1), add(y, x, -1);
else add(x, y, 1), add(y, x, 1);
e.push_back(mp(x, y));
}
}
inline void floyd(){
rep(i, 1, n) f[i][i]=0;
rep(k, 1, n) rep(i, 1, n) rep(j, 1, n)
f[i][j]=min(f[i][j], f[i][k]+f[k][j]);
}
int val[maxn+5];
signed main(){
input();
floyd();
rep(i, 1, n) if(f[i][i]<0)
return printf("nO\n"), 0;
int ans=-1, op;
rep(i, 1, n){
int mx=-inf, mn=inf;
rep(j, 1, n) mx=max(mx, f[i][j]), mn=min(mn, f[i][j]);
for(pii cur: e) if(f[i][cur.fi]==f[i][cur.se])
mx=-inf;
if(ans<mx-mn) ans=mx-mn, op=i;
}
if(ans==-1) return printf("nO\n"), 0;
printf("yEs\n%d\n", ans);
int mx=inf, mn=-inf;
rep(j, 1, n) mx=max(mx, f[op][j]), mn=min(mn, f[op][j]);
val[op]=1e5;
rep(j, 1, n) if(j!=op)
val[j]=val[op]+f[op][j];
rep(i, 1, n) writc(val[i], ' ');
Endl;
return 0;
}
肆、关键之处 ¶
连边的深层含义:从 \(u\) 向 \(v\) 连一条 \(w\) 的边,即保证 \(v\le u+w\),否则 \(v\) 就会被 \(u+w\) 更新。