差分約束系統詳解 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;
}