1. 程式人生 > >差分約束系統詳解 BZOJ 2330 糖果

差分約束系統詳解 BZOJ 2330 糖果

差分約束系統

有一個男人他是這樣說的
我們有如下幾個式子:
A-B<=x —-> A最多比B大x
A-C<=y —-> A最多比C大y
B-C<=z —-> B最多比C大z
所有的約束條件反映了一個問題:
一個數不可能過大,因為某個數可能至多比某個數大k,所以這是一類有最大值問題。
那麼假如對於A-B<=x ,我們由B向A連一條大小為x的邊。
對於所有等式,由C向B連一條z的邊,B向A連一條x的邊,C向A連一條y的邊。
我們看一下,C到A的路徑有兩條,一條長度為y的代表A至多比C大y。
另一條路徑C–>B–>A的長度為x+z,代表A至多比C大x+z。
最後我們發現C到A的最短路徑就是C至多比A大多少,即有最大值限制。
跑SPFA求最短路,記得判斷負權迴路。

可是另一個男人他是這樣說的
A-B>=x —-> A至少比B大x
A-C>=0 —-> A至少和C相等
B-C>=0,C-B>=0 —-> B的值等於C的值
所有的約束條件也反映了一個問題:
一個數不可能過小,因為某個數至少要比某個數大,所以這是一類有最小值問題。
我們對於A-B>=x,我們由B向A連一條大小為x的邊。
我們從B走向A的最長(若有正環代表迴圈約束約束失敗)路徑就是A至少比B大多少。
我們跑SPFA最長路徑,記得判斷正環。

對於BZOJ2330我們發現它是第二類問題
我們首先創造出一個0號節點指向每個節點大小為1代表每個小朋友至少分一個。
然後五個式子都可以化簡成A-B>=X或B-A>=X,然後跑一下即可。

這題不用int會超,所以注意這句話:typedef int ll;開始其實代的long long……
還有0向n個小盆友的邊要從大(n)到小(1)連,否則超時,大爺告訴我的……
@PoPoQQQ,還好告訴我了要不得T一晚上!?F:>:@??>!:”>LE:>@!!

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<queue>
#include<vector>
#include<climits> #include<string> #include<cstdlib> #include<set> #include<stack> #include<map> #include<bitset> #include<ctime> using namespace std; typedef int ll; typedef unsigned long long ull; inline ll read() { char k=0;char ls;ls=getchar();for(;ls<'0'||ls>'9';k=ls,ls=getchar()); ll x=0;for(;ls>='0'&&ls<='9';ls=getchar())x=x*10+ls-'0'; if(k=='-')x=0-x;return x; } struct E{ ll next; ll zhi; ll s; }e[400500]; ll last[100500]; ll n,m,w; ll dis[100500]; ll vis[100500]; ll pan[100500]; long long ans; queue<ll>q; bool spfa() { q.push(0); ++vis[0]; pan[0]=1; while(!q.empty()) { ll d=q.front(); q.pop(); pan[d]=0; for(int j=last[d];j;j=e[j].zhi) { ll z=e[j].next; if(dis[z]<dis[d]+e[j].s) { dis[z]=dis[d]+e[j].s; ++vis[z]; if(vis[z]>(n+1)) return 0; if(pan[z]==0) { q.push(z); pan[z]=1; } } } } return 1; } void Add(ll a,ll b,ll s) { e[++w].next=b; e[w].s=s; e[w].zhi=last[a]; last[a]=w; return; } int main() { n=read(),m=read(); for(int i=1;i<=m;++i) { ll x=read(),a=read(),b=read(); switch(x) { case 1: Add(a,b,0); Add(b,a,0); break; case 4: swap(a,b); case 2: Add(a,b,1); break; case 3: swap(a,b); case 5: Add(a,b,0); break; } } for(int i=n;i>=1;--i) Add(0,i,1); if(!spfa()) cout<<-1<<endl; else { for(int i=1;i<=n;++i) {ans+=dis[i];} cout<<ans<<endl; } return 0; }