并查集-水晶球(nkoj2105)

并查集-水晶球(nkoj2105)

题意分析

动态维护[l,r]区间的和

并查集与前缀和结合使用

利用带序号的并查集与前缀和

这里将i节点的到它的父亲fi这一区间的和记作dis[i],将x的前缀和记作 S x

有如下性质:

  • S x S y = p x的前缀和与y的前缀和之差,即[y,x]区间的和为p
  • d i s [ x ] = S x S f x [fx,x]区间的和为dis[x]
  • d i s [ y ] = S y S f y [fy,y]区间的和为dis[y]

可以得到合并的时候:

  • d i s [ f x ] = S f x S f y = d i s [ x ] + d i s [ y ] + p 即[fx,fy]区间([fx,ffx]区间)的和为-dis[x] + dis[y] + p

这样维护序号,如果两个节点属于同一堆,则可得到他们组成的区间的和

代码

//
// Created by rv on 2018/4/20.
//

#include "cstdio"
#include "map"

using namespace std;

struct UF {
    int count;
    int* father;
    int* dis;
    UF(int t) {
        count  = t;
        father = new int[t];
        dis = new int[t];
        for (int i = 0; i < t; i++) {
            father[i] = i;
            dis[i] = 0;
        }
    }
    int get_father(int x) {
        if (father[x] == x) {
            return x;
        } else {
            int tmp = get_father(father[x]);
            dis[x] += dis[father[x]];
            father[x] = tmp;
            return father[x];
        }
    }
    void merge(int x, int y, int p) {
        int fx = get_father(x);
        int fy = get_father(y);
        father[fx] = fy;
        dis[fx] = - dis[x] + dis[y] + p;
    }
    void check(int x, int y, int p) {
        int fx = get_father(x);
        int fy = get_father(y);
        if (fx == fy) {
            int can_p = dis[x] - dis[y];
            if (can_p != p) {
                printf("Bug Detected %d\n", can_p);
            } else {
                printf("Accept\n");
            }
        } else {
            merge(x, y, p);
            printf("Accept\n");
        }
    }
    void print() {
        printf("father: ");
        for (int i = 0; i < count; i++) {
            printf("%d ", father[i]);
        }
        printf("\n");
        // printf("size: ");
        // for (int i = 0; i < count; i++) {
            // printf("%d ", size[i]);
        // }
        // printf("\n");
        printf("dis: ");
        for (int i = 0; i < count; i++) {
            printf("%d ", dis[i]);
        }
        printf("\n");
    }
};

int main() {
//    freopen("1206/data2.in", "r", stdin);
//    freopen("data2.out", "w", stdout);
    int M, l, r, p;
    scanf("%d", &M);
    UF uf(2 * M + 1);
    map<int, int> mp;
    while(M--) {
        scanf("%d%d%d", &l, &r, &p);
        // printf("l=%d r=%d p=%d\n", l, r, p);
        if (mp.find(r) == mp.end()) {
            mp[r] = mp.size();
        }
        if (mp.find(l - 1) == mp.end()) {
            mp[l - 1] = mp.size();
        }
        uf.check(mp[r], mp[l - 1], p);
//        uf.print();
    }
    return 0;
}

PS

由于l,r太大,所以用map映射(离散化)后再用并查集处理较好,具体做法就是mp[i]=mp.size()

猜你喜欢

转载自blog.csdn.net/xenoncat/article/details/80183015