2330: [SCOI2011]糖果
Description
幼儿园里有N个小朋友,lxhgww老师现在想要给这些小朋友们分配糖果,要求每个小朋友都要分到糖果。但是小朋友们也有嫉妒心,总是会提出一些要求,比如小明不希望小红分到的糖果比他的多,于是在分配糖果的时候,lxhgww需要满足小朋友们的K个要求。幼儿园的糖果总是有限的,lxhgww想知道他至少需要准备多少个糖果,才能使得每个小朋友都能够分到糖果,并且满足小朋友们所有的要求。
Input
输入的第一行是两个整数N,K。
接下来K行,表示这些点需要满足的关系,每行3个数字,X,A,B。
如果X=1, 表示第A个小朋友分到的糖果必须和第B个小朋友分到的糖果一样多;
如果X=2, 表示第A个小朋友分到的糖果必须少于第B个小朋友分到的糖果;
如果X=3, 表示第A个小朋友分到的糖果必须不少于第B个小朋友分到的糖果;
如果X=4, 表示第A个小朋友分到的糖果必须多于第B个小朋友分到的糖果;
如果X=5, 表示第A个小朋友分到的糖果必须不多于第B个小朋友分到的糖果;
Output
输出一行,表示lxhgww老师至少需要准备的糖果数,如果不能满足小朋友们的所有要求,就输出-1。
Sample Input
5 7
1 1 2
2 3 2
4 4 1
3 4 5
5 4 5
2 3 5
4 5 1
Sample Output
11
HINT
【数据范围】
对于30%的数据,保证 N<=100
对于100%的数据,保证 N<=100000
对于所有的数据,保证 K<=100000,1<=X<=5,1<=A, B<=N
本来是想着拓扑排序的
还是要学新东西
先讲讲差分约束系统吧。
如果有3个表达式
a - b <= k1
b - c <= k2
a - c <= k3
那么我们现在要求a - c的最小值 显然 a - c <= min(k1 + k2, k3)
那么由此我们想到了最短路。
再看本题 总共五个条件,为了好处理,可以这样:
a - b >= k1
b - c >= k2
a - c >= k3
改为求a - c的最小值 同理 a - c >= max(k1 + k2, k3)
那么就相当于求最长路
本题中的条件可以转化为
1: a - b >= 0 && b - a >= 0
2: b - a >= 1
3: a - b >= 0
4: a - b >= 1
5: b - a >= 0
如此加边, 起点为节点0。它对每个节点的约束条件为: ai - a0 >= 0
最后spfa
附代码
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <cstring>
#include <queue>
#define N 100010
using namespace std;
struct edge {
int to, key, next;
edge(void) {}
edge(int t, int k, int n):to(t), key(k), next(n) {}
}G[N << 2];
int head[N], vis[N], dist[N], innum[N];
int n, m, x, a, b, cnt = 0;
long long ans = 0;
queue<int> Q;
inline void add_edge(int from, int to, int key) {
G[cnt] = edge(to, key, head[from]);
head[from] = cnt++;
}
inline char get(void) {
static char buf[100000], *p1 = buf, *p2 = buf;
if (p1 == p2) {
p2 = (p1 = buf) + fread(buf, 1, 100000, stdin);
if (p1 == p2) return EOF;
}
return *p1++;
}
inline void read(int &x) {
x = 0; char c;
for(c = get(); c < '0' || c > '9'; c = get());
for(; c >= '0' && c <= '9'; x = (x << 1) + (x << 3) + c - '0', c = get());
}
bool spfa(int s) {
memset(dist, 0, sizeof(dist));
memset(vis, 0, sizeof(vis));
memset(innum, 0, sizeof(innum));
Q.push(s); vis[s] = 1; dist[s] = 0; innum[s]++;
while(!Q.empty()) {
int x = Q.front(); Q.pop();
for(int i = head[x]; i != -1; i = G[i].next) {
edge &e = G[i];
if (dist[e.to] < dist[x] + e.key) {
dist[e.to] = dist[x] + e.key;
if (!vis[e.to]) {
Q.push(e.to); vis[e.to] = 1;
if (++innum[e.to] >= n) return 1;
}
}
}
vis[x] = 0;
}
return 0;
}
int main(void) {
read(n); read(m);
memset(head, -1, sizeof(head));
for(int i = 0; i < m; i++) {
read(x); read(a); read(b);
switch (x) {
case 1: {
add_edge(a, b, 0); add_edge(b, a, 0);
break;
}
case 2: {
if (a == b) {puts("-1"); return 0;}
add_edge(a, b, 1); break;
}
case 3: {
add_edge(b, a, 0); break;
}
case 4: {
if (a == b) {puts("-1"); return 0;}
add_edge(b, a, 1); break;
}
case 5: {
add_edge(a, b, 0); break;
}
}
}
for(int i = n; i > 0; i--) add_edge(0, i, 1);
if (spfa(0)) {puts("-1"); return 0;}
for(int i = 1; i <= n; i++) ans += dist[i];
printf("%lld\n", ans);
return 0;
}