Bzoj P2330 [SCOI2011]糖果___差分约束系统

27 篇文章 0 订阅
3 篇文章 0 订阅

题目大意:

N个人要分糖果,要求每个小朋友都要分到糖果,且要满足 K K K个要求,
对于每个要求给出一组 X , A , B X,A,B X,A,B
X = 1 X=1 X=1, 表示第 A A A个小朋友分到的糖果必须和第 B B B个小朋友分到的糖果一样多;
X = 2 X=2 X=2, 表示第 A A A个小朋友分到的糖果必须少于第 B B B个小朋友分到的糖果;
X = 3 X=3 X=3, 表示第 A A A个小朋友分到的糖果必须不少于第 B B B个小朋友分到的糖果;
X = 4 X=4 X=4, 表示第 A A A个小朋友分到的糖果必须多于第 B B B个小朋友分到的糖果;
X = 5 X=5 X=5, 表示第 A A A个小朋友分到的糖果必须不多于第 B B B个小朋友分到的糖果;

问至少需要准备多少个糖果,才能使得每个小朋友都能够分到糖果,并且满足小朋友们所有的要求。

K &lt; = 100000 , 1 &lt; = X &lt; = 5 , 1 &lt; = A , B &lt; = N K&lt;=100000,1&lt;=X&lt;=5,1&lt;=A, B&lt;=N K<=1000001<=X<=51<=A,B<=N

分析:

明细的差分约束系统,
①A必须和B分的一样多
连边 A − &gt; B , B − &gt; A , 边 权 为 0 A-&gt;B,B-&gt;A,边权为0 A>BB>A0,即 A A A分的必须等于 B B B分的
②A分的必须少于B的个
连边 A − &gt; B , 边 权 为 1 A-&gt;B,边权为1 A>B1,即 B B B分的至少为 A A A分的 + 1 +1 +1
③A分的必须不少于B分的
连边 B − &gt; A , 边 权 为 0 B-&gt;A,边权为0 B>A0,即 A A A分的至少等于 B B B分的
④A分的必须多于B分的
连边 B − &gt; A , 边 权 为 1 B-&gt;A,边权为1 B>A1,即 A A A分的至少为 B B B分的 + 1 +1 +1
⑤A分的必须不多于B分的
连边 A − &gt; B , 边 权 为 0 A-&gt;B,边权为0 A>B0,即 B B B分的至少等于 A A A分的
建立一个起点 S S S,连向所有的人,边权为 1 1 1(每个都要分到至少一个),然后跑最长路即可
最后 Σ i = 1 n d i s [ i ] Σ_{i=1}^{n}dis[i] Σi=1ndis[i]即为 A n s w e r Answer Answer
有负环或者自环就输出 − 1 -1 1

代码:

#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <queue>
#include <cstdlib>
#include <algorithm>

#define N 100005

using namespace std;

typedef long long ll;

struct Node { int To, w, nxt; }e[N*10];
int dis[N], ls[N], Ru[N], n, m, cnt, S = 0;
bool vis[N];

queue <int> Q;

void read(int &x)
{
    int f = 1; x = 0; char s = getchar();
    while (s < '0' || s > '9')   { if (s == '-')   f = -1; s = getchar(); }
    while (s >= '0' && s <= '9') { x = x * 10 + (s - '0'); s = getchar(); }
    x = x * f;
}

void Addedge(int u, int v, int w)
{
    e[++cnt].To = v, e[cnt].w = w, e[cnt].nxt = ls[u], ls[u] = cnt;
}

bool Spfa()
{
	while (!Q.empty()) Q.pop();
	vis[S] = 1; Q.push(S);
    while (!Q.empty())
	{
        int u = Q.front(); Q.pop();
        for(int i = ls[u]; i; i = e[i].nxt)
            if (dis[u] + e[i].w > dis[e[i].To])
			{
                dis[e[i].To] = dis[u] + e[i].w;
				++Ru[e[i].To]; if (Ru[e[i].To] > n) return 1;
				if(!vis[e[i].To]) 
				   vis[e[i].To] = 1, Q.push(e[i].To);
            }
        vis[u] = 0;
    }
    return 0;
}

int main()
{
    read(n); read(m);
    for (int i = 1; i <= m; i++)
	{
		int opt, A, B; 
        read(opt); read(A); read(B); 
        if (opt == 1) Addedge(A, B, 0), Addedge(B, A, 0);
        if (opt == 2)
        {
            if (A == B) { printf("-1\n"); return 0; }
            Addedge(A, B, 1);
        }
        if (opt == 3) Addedge(B, A, 0);
        if (opt == 4)
		{
            if (A == B) { printf("-1\n"); return 0; }
            Addedge(B, A, 1);
        }
        if (opt == 5) Addedge(A, B, 0);
    }
    
    for (int i = n; i >= 1; i--) Addedge(S, i, 1);
    if (Spfa()) printf("-1\n");
       else 
       {
       	    ll Answer = 0;
       	    for (int i = 1; i <= n; i++) Answer = Answer + dis[i];
       	    printf("%lld\n", Answer);
	   }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值