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;
}

浙公网安备 33010602011771号