【洛谷】P3275 糖果

题目地址:

https://www.luogu.com.cn/problem/P3275

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

输入格式:
输入的第一行是两个整数 N N N K K K。接下来 K K K行,表示这些点需要满足的关系,每行 3 3 3个数字, X X X A A A B B 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个小朋友分到的糖果;

输出格式:
输出一行,表示 lxhgww \text{lxhgww} lxhgww老师至少需要准备的糖果数,如果不能满足小朋友们的所有要求,就输出 − 1 -1 1

数据范围:
对于 30 % 30\% 30%的数据,保证 N ≤ 100 N\leq100 N100
对于 100 % 100\% 100%的数据,保证 N ≤ 100000 N\leq100000 N100000
对于所有的数据,保证 K ≤ 100000 , 1 ≤ X ≤ 5 , 1 ≤ A , B ≤ N K\leq100000, 1\leq X\leq5, 1\leq A, B\leq N K100000,1X5,1A,BN

标准做法是用最长路求差分约束,这里还可以用缩点 + 拓扑排序来做。先按差分约束最长路的手法建图(该图里每条边都非负),然后在只考虑 0 0 0边的情况下缩点,这样每个团的糖果数应该相等。接着建出缩点后的图,如果某个团内部有非零边,则无解;否则可以利用拓扑排序来求最长路,然后累加起来即可。代码如下:

#include <iostream>
#include <cstring>
#include <queue>
using namespace std;

const int N = 1e5 + 10, M = 4e5 + 10;
int n, m;
int h[N], hs[N], e[M], ne[M], w[M], idx;
int dfn[N], low[N], timestamp;
int stk[N], top;
bool in_stk[N];
int id[N], scc_cnt, sz[N];
int ind[N], dist[N];
queue<int> q;

void add(int a, int b, int c, int h[]) {
  e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
}

void tarjan(int u) {
  dfn[u] = low[u] = ++timestamp;
  stk[top++] = u, in_stk[u] = true;
  for (int i = h[u]; ~i; i = ne[i]) {
    if (w[i]) continue;
    int v = e[i];
    if (!dfn[v]) {
      tarjan(v);
      low[u] = min(low[u], low[v]);
    } else if (in_stk[v]) low[u] = min(low[u], dfn[v]);
  }

  if (dfn[u] == low[u]) {
    scc_cnt++;
    int y;
    do {
      y = stk[--top];
      in_stk[y] = false;
      id[y] = scc_cnt;
      sz[scc_cnt]++;
    } while (y != u);
  }
}

int main() {
  memset(h, -1, sizeof h);
  memset(hs, -1, sizeof hs);
  scanf("%d%d", &n, &m);
  for (int i = 1; i <= n; i++) add(0, i, 0, h);
  while (m--) {
    int op, a, b;
    scanf("%d%d%d", &op, &a, &b);
    // 这里提前特判一下无解的情形
    if (a == b) {
      if (op == 2 || op == 4) {
        puts("-1");
        return 0;
      }
      continue;
    }
    if (op == 1) add(a, b, 0, h), add(b, a, 0, h);
    else if (op == 2) add(a, b, 1, h);
    else if (op == 3) add(b, a, 0, h);
    else if (op == 4) add(b, a, 1, h);
    else add(a, b, 0, h);
  }

  // 缩点
  for (int i = 1; i <= n; i++)
    if (!dfn[i])
      tarjan(i);

  // 建立缩点后的图
  for (int i = 1; i <= n; i++)
    for (int j = h[i]; ~j; j = ne[j]) {
      int x = id[i], y = id[e[j]];
      if (x != y) {
        ind[y]++;
        add(x, y, w[j], hs);
      } else if (w[j]) {
        puts("-1");
        return 0;
      }
    }

  // 拓扑排序
  for (int i = 1; i <= scc_cnt; i++)
    if (!ind[i]) {
      q.push(i);
      dist[i] = 1;
    }

  n = 0;
  while (q.size()) {
    n++;
    int t = q.front(); q.pop();
    for (int i = hs[t]; ~i; i = ne[i]) {
      int v = e[i];
      dist[v] = max(dist[v], dist[t] + w[i]);
      ind[v]--;
      if (!ind[v]) q.push(v);
    }
  }

  // 有环,说明无解
  if (n < scc_cnt) puts("-1");
  else {
    long res = 0;
    for (int i = 1; i <= scc_cnt; i++) res += (long) sz[i] * dist[i];
    printf("%ld\n", res);
  }
  return 0;
}

时间复杂度 O ( N + K ) O(N+K) O(N+K),空间 O ( N ) O(N) O(N)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值