[题解] 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]; //在同一集合中判断
}

浙公网安备 33010602011771号