差分約束系統詳解 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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章