小a与军团模拟器

 

题目描述

9102

年伊始,小a觉得山羊模拟器,乞丐模拟器之类的都太低级了,所以想自己建立一个征战天下的军团模拟器。
军团模拟器是在一个城市数为N的国家中运行的,每个城市都会通过一些道路和其他所有城市相连,道路总数为N1。 开始时,每个城市中都会有一个军队,每个军队有着自己的编号。
定义军团为相邻的同种编号军队的最大联通块,有些时候某种编号的军队会改变自己的编号,小a想要知道这些时候整个国家有多少军团。 形式化的,我们会有Q次操作,每次操作为以下形式
一行两个正整数a,b表示所有编号为a的军队编号变成b

 

输入描述:

第一行两个整数N,Q

接下来一行N
个非负整数,表示初始时N
个城市上军队的编号是多少,如果为0
那么无军队。
接下来N1
行每行两个正整数u,v
表示城市u
和城市v
之间有道路相连。
接下来Q
行,两个整数x,y
表示询问操作。

输出描述:

对于每次询问,输出一行一个整数,表示询问过后的军团数。
示例1

输入

5 2
1 1 3 2 2
1 2
1 3
2 4
2 5
3 2
2 1

输出

复制
4
1

说明

初始时共有四个军团,分别为{1,2}{3}{4}{5}

操作一使得3号节点的编号由3改为2,此时仍有四个军团
操作二使得4,5号节点的编号由2改为1,此时只有一个军团
示例2

输入

5 6
1 2 3 4 9
1 2
2 3
3 4
4 5
1 2
2 1
2 3
1 3
3 4
4 5

输出

4
4
4
3
2
2

备注:

保证1N,Q,200000
 
保证输入数据合法
 
思路:启发式合并
暴力做法是存一下每一种颜色对应的节点,然后统计一下和它相邻的颜色不同的节点个数并减去,修改之后再统计一遍相邻的不同颜色的节点个数,再加上。
如果把节点很多的修改成很少的,这样还是会炸。
考虑启发式合并,把颜色少的并到多的上,再用一个数组映射一下该颜色对应的是什么颜色就行了
我不会复杂度分析。
题解原话:每个点的复杂度是他的度数乘以它被统计答案的次数,最差情况下每个点也只会被统计logN次答案,最终复杂度为O(𝑁𝑙𝑜𝑔𝑁)
#include <bits/stdc++.h>
#define pb push_back
using namespace std;

template<typename T>
inline void read(T &x) {
    x = 0;T f = 1; char ch = getchar();
    while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9') { x = x * 10 + ch - 48; ch = getchar(); }
    x *= f;
}

const int N = 2e5 + 10;
int n, col[N], ans, mp[N];
vector<int> color[N], G[N];

void dfs(int u, int fa) {
    for (auto v: G[u]) {
        if (v == fa) continue;
        if (col[v] && col[v] != col[u]) ans++;
        dfs(v, u);
    }
}

void solve(int &x, int &y) {
    if (color[x].size() > color[y].size()) swap(x, y);
    for (auto u: color[x])
        for (auto v: G[u]) {
            if (col[v] && col[v] != x) ans--;
        }
    for (auto u: color[x]) {
        col[u] = y;
        color[y].pb(u);
    }
    for (auto u: color[x])
        for (auto v: G[u]) 
            if (col[v] && col[v] != y) ans++;
    color[x].clear();
    x = -1;
}

int main() {
    int q;
    read(n); read(q);
    for (int i = 1; i <= n; i++) read(col[i]), color[col[i]].pb(i), mp[i] = i;
    for (int i = 1; i < n; i++) {
        int u, v;
        read(u), read(v);
        G[u].pb(v); G[v].pb(u);
    }
    if (col[1] != 0) ans++;
    dfs(1, 1);
    while (q--) {
        int x, y;
        read(x), read(y);
        if (mp[y] < 0) mp[x] = -1, mp[y] = x;
        else if (mp[x] >= 0 && mp[x] != mp[y]) solve(mp[x], mp[y]);
        printf("%d\n", ans);
    }
    return 0;
}
View Code

 

posted @ 2019-07-11 18:57  Mrzdtz220  阅读(286)  评论(0)    收藏  举报