并查集 hdu3038 How Many Answers Are Wrong

传送门:点击打开链接

题意:有Q次操作,每次操作告诉你l,r,s,表示区间[l,r]里的和为s。如果某次操作与之前得到的内容冲突,就无视这次操作,最后输出冲突的操作次数。

思路:首先我们可以证明,只有当[l,r]存在多种情况被小区间恰好覆盖时,然后多种情况的小区间之和不相等时,就认为是冲突的。

我们能比较容易的想到把l-1,和r去维护并查集,但是如何维护s,是个问题

如果按秩合并,我们可以用边长来维护额外的信息,但是按秩合并的边,维护的信息,是最后两个连通块合并时的信息。

而且通常会与lca和路径求和结合起来,如果非要强行做这道题,那就是lct了,或者按秩合并+离线lca也能搞。

实际上,并不需要这么复杂的编程。

我们来维护节点到树根的距离。那么对于一棵树上面的两个节点,如果这棵树的根接到了其他的树上,他们的距离可能会变化。

但是,他们到树根距离的差,是不会变的!所以我们这里来维护节点到树根的距离,利用他们的相减是恒定的,来维护答案。

#include <map>
#include <set>
#include <cmath>
#include <ctime>
#include <stack>
#include <queue>
#include <cstdio>
#include <cctype>
#include <bitset>
#include <string>
#include <vector>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <functional>
#define fuck(x) cout<<"["<<x<<"]";
#define FIN freopen("input.txt","r",stdin);
#define FOUT freopen("output.txt","w+",stdout);
//#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;

const int MX = 2e5 + 5;
int P[MX], val[MX];
int find(int x) {
    if(P[x] == x) return x;
    int p = find(P[x]);
    val[x] += val[P[x]];
    return P[x] = p;
}
int solve(int l, int r, int s) {
    int u = find(l), v = find(r);
    if(u != v) {
        P[v] = u; val[v] = s + val[l] - val[r];
        return 0;
    } else return s != val[r] - val[l];
}
int main() {
    int n, m; //FIN;
    while(~scanf("%d%d", &n, &m)) {
        for(int i = 0; i <= n; i++) {
            P[i] = i; val[i] = 0;
        }
        int ans = 0;
        for(int i = 1; i <= m; i++) {
            int l, r, s;
            scanf("%d%d%d", &l, &r, &s); l--;
            ans += solve(l, r, s);
        }
        printf("%d\n", ans);
    }
    return 0;
}


你可能感兴趣的:(并查集 hdu3038 How Many Answers Are Wrong)