差分约束系统详解 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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值