[题解] HDU 3038 How Many Answers Are Wrong

[题解] HDU 3038 How Many Answers Are Wrong

·题目大意

有一段长度为 \(n\) 的序列 \(S\),现在给定一共 \(m\) 条信息。
每条信息格式为 \(l\) \(r\) \(v\)
表示 \(sum(l,r)=v\) 。显然,这 \(m\) 条信息中有一些是与之前矛盾的,
现在请你输出一共有多少条矛盾的信息。
\(1\leq n \leq 2 \times 10^5\)\(1\leq m \leq 4 \times 10^4\), 有多组数据

·解题思路

看到这种题目,第一眼想到的就是差分约束,但是首先图要支持动态维护边,并且单次时间复杂度为 \(O(nm)\) ,显然不可行。
既然不能用差分约束,那么我们就想到了用带权并查集来维护信息。
假设我们用 \(fa[x]\) 来表示 \(x\) 的父节点,数组 \(val[x]\) 来表示节点 \(x\) 到根节点的权值,并且在合并时固定使得 \(fa[x] = y\)
首先,我们要把闭区间变为开区间 \([l,r]\) --> \([l,r+1)\)
那么当我们处理一条信息 \(l\) , \(r\) , \(v\) 时,有两种情况:

  • 在同一个并查集合,这时如果不矛盾,
    则有 \(v + val[r] = val[l]\)
    所以只需判断 \(v == val[l] - val[r]\) 即可。
  • 不在同一个并查集,我们要把它们合并,并把被合并的根节点更新,
    这时 \(l\) 到新的根节点的距离 \(d\) 有两种方法表示:
    \(d = v + val[r]\)\(d = val[l] + val[find(l)]\)
    其中 \(find(l)\) 为更新前 \(l\) 的根节点。
    那么只需更新 \(val[find(l)] = v + val[r] - val[l]\) 即可。
  • 单次时间复杂度为 \(O(n \times \alpha(n))\)

·代码实现

#include <iostream>
#define reg register
using namespace std;
namespace io {
template<typename T>inline void read(T &x) {
  char ch, f = 0; x = 0;
  while (!isdigit(ch = getchar())) f |= ch == '-';
  while (isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar();
  x = f? -x: x;
}
char ch[20];
template<typename T>inline void write(T x) {
  (x < 0) && (x = -x, putchar('-'));
  x || putchar('0');
  char i = 0;
  while (x) ch[i++] = x % 10 ^ 48, x /= 10;
  while (i) putchar(ch[--i]);
}
}
#define rd io::read
#define wt io::write
const int maxN = 200010;
int fa[maxN], val[maxN];
int n, m, ans;
int find(int);
bool merge(int, int, int);
int main() {
  while(~scanf("%d%d", &n, &m)) {
    ans = 0;
    for (reg int i = 1; i <= n + 1; ++i) fa[i] = i, val[i] = 0;
    for (reg int i = 1, x, y, z; i <= m; ++i) {
      rd(x); rd(y); rd(z);
      ans += merge(x, y + 1, z);
    }
    wt(ans); putchar('\n');
  }
  return 0;
}
int find(int x) {
  if (x == fa[x]) return x;
  int k = fa[x];
  fa[x] = find(fa[x]); val[x] += val[k]; //更新权值并路径压缩
  return fa[x];
}
bool merge(int x,int y,int v) {
  int a = find(x), b = find(y);
  if (a ^ b) { //不在同一集合中合并并更新,其中 a ^ b 相当于 a != b
    fa[a] = b;
    val[a] = v + val[y] - val[x];
    return false;
    //v + val[y] == val[a] + val[x]
  }
  return v ^ val[x] - val[y]; //在同一集合中判断
}
posted @ 2021-09-15 20:53  LitDarkness  阅读(124)  评论(0)    收藏  举报