[CF1450E]Capitalism

壹、题目描述 ¶

传送门 to CF.

贰、题解 ¶

这个输入真的很......差分约束。

回忆差分约束的细节,构造形如 \(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\) 更新。

posted @ 2021-07-16 22:34  Arextre  阅读(61)  评论(0编辑  收藏  举报