ABC补题集

T1(批量式kruskal,增量式的nb应用)

ABC355F.
这题边权巨小。只有10。考虑从此处下手。这里考虑kruskal的过程,我们一开始的想法是,不断加权值最小的边,但是这里显然有冗余,我们没有必要一个个取吧?考虑一次把x边取完。也就是开10批,当然开并查集维护连通关系,也就是维护出这10个并查集。对于问询,能不能增量式维护?当然就可以了!!!!!这就是增量式nb的地方。我们可以增量式维护这10个并查集!!!这就是说,本来每改一个数,我们都要重新做一遍,实际上没有这个必要。只会影响w~n的并查集,我们就维护好了。只要把边权分类,就没必要重新做一遍了。
对于答案。对于x+1,到底选了多少呢?当然是x连通块个数-x+1连通块个数啦。(原来选了的不再选,新选的会减1)。

类似题目:


同样考虑转移一批。然后实际上也是增量式维护了并查集。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int N = 200010;

int n, q;
int p[20][N], cnt[20];

int find(int i, int a)
{
    if (p[i][a] == a) return p[i][a];
    return p[i][a] = find(i, p[i][a]);
}

int main()
{
    scanf("%d%d", &n, &q);
    for (int i = 1; i <= n; i ++ )
        for (int j = 1; j <= 10; j ++ )
            p[j][i] = i;
    for (int i = 0; i <= 10; i ++ ) cnt[i] = n;
    for (int j = 0; j < n - 1; j ++ )
    {
        int a, b, w;
        scanf("%d%d%d", &a, &b, &w);

        for (int i = w; i <= 10; i ++ )
            p[i][find(i, a)] = find(i, b), cnt[i] -- ; 
    }

    while (q -- )
    {
        int a, b, w;
        scanf("%d%d%d", &a, &b, &w);
        
        for (int i = w; i <= 10; i ++ )
            if (find(i, a) != find(i, b)) p[i][find(i, a)] = find(i, b), cnt[i] -- ;
        int res = 0;
        for (int i = 1; i <= 10; i ++ )
            res += (cnt[i - 1] - cnt[i]) * i;
        
        printf("%d\n", res);
    }
    return 0;
}
posted @ 2024-05-29 22:07  hhhhhua  阅读(14)  评论(0)    收藏  举报