题目链接

题目

题目描述

幼儿园里有N个小朋友,lxhgww老师现在想要给这些小朋友们分配糖果,要求每个小朋友都要分到糖果。但是小朋友们也有嫉妒心,总是会提出一些要求,比如小明不希望小红分到的糖果比他的多,于是在分配糖果的时候,lxhgww需要满足小朋友们的K个要求。幼儿园的糖果总是有限的,lxhgww想知道他至少需要准备多少个糖果,才能使得每个小朋友都能够分到糖果,并且满足小朋友们所有的要求。

输入描述

输入的第一行是两个整数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个小朋友分到的糖果;

输出描述

输出一行,表示lxhgww老师至少需要准备的糖果数,如果不能满足小朋友们的所有要求,就输出-1。

示例1

输入

5 7
1 1 2
2 3 2
4 4 1
3 4 5
5 4 5
2 3 5
4 5 1

输出

11

备注

对于30%的数据,保证 \(N\leq100\)
对于100%的数据,保证 \(N\leq100000\)
对于所有的数据,保证 \(K\leq100000, 1\leq X\leq5, 1\leq A, B\leq N\)

题解

知识点:差分约束。

这是一道差分约束模板题。

求最小值用最长路。关于建边利用不等式:\(u\to v\) 权为 \(w\) ,则 \(dis[u]\geq dis[v]+w\)

如果存在正环,则无解。

时间复杂度 \(O(n+km)\)

空间复杂度 \(O(n+m)\)

代码

#include <bits/stdc++.h>
#define ll long long

using namespace std;

const int N = 100007, K = 100007;

template<class T>
struct Graph {
    struct edge {
        int v, nxt;
        T w;
    };
    int idx;
    vector<int> h;
    vector<edge> e;

    Graph(int n, int m) :idx(0), h(n + 1), e(m + 1) {}

    void add(int u, int v, T w) {
        e[++idx] = edge{ v,h[u],w };
        h[u] = idx;
    }
};
Graph <int> g(N, 3 * K);///一号坑,超级源点连边多了N个,原本边可能全是等于有2K个

int n, k;

bool vis[N];
int dis[N], cnt[N];
stack<int> q;///二号坑,queue会被卡入点顺序
bool SPFA(int s) {
    memset(dis, -1, sizeof(dis));
    q.push(s);
    dis[s] = 0;
    vis[s] = 1;
    while (!q.empty()) {
        int u = q.top();
        q.pop();
        vis[u] = 0;
        for (int i = g.h[u];i;i = g.e[i].nxt) {
            int v = g.e[i].v;
            int w = g.e[i].w;
            if (dis[v] < dis[u] + w) {
                dis[v] = dis[u] + w;
                cnt[v] = cnt[u] + 1;
                if (cnt[v] >= n + 1) return false;///三号坑,多了个超级源点,所以n+1
                if (!vis[v]) {
                    q.push(v);
                    vis[v] = 1;
                }
            }
        }
    }
    return true;
}


int main() {
    std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    cin >> n >> k;
    for (int i = 1;i <= k;i++) {
        int x, a, b;
        cin >> x >> a >> b;
        if (x == 1) {
            g.add(a, b, 0);
            g.add(b, a, 0);
        }
        else if (x == 2) {
            g.add(a, b, 1);
        }
        else if (x == 3) {
            g.add(b, a, 0);
        }
        else if (x == 4) {
            g.add(b, a, 1);
        }
        else if (x == 5) {
            g.add(a, b, 0);
        }
    }
    for (int i = 1;i <= n;i++) g.add(n + 1, i, 1);///超级源点,为了方便,距离为0,到其他点为1,表示其他点>=0+1
    /// 正边最长路用SPFA,A->B == dis[A] + w <= dis[B] 
    if (!SPFA(n + 1)) cout << -1 << '\n';
    else {
        ll ans = 0;///四号坑,最大值n^2/2
        for (int i = 1;i <= n;i++) ans += dis[i];
        cout << ans << '\n';
    }
    return 0;
}