Codeforces 734E(DFS缩点+树的直径)

Codeforces 734E
题意:有一棵树被黑白染色,同色的为一个联通块,每次可以让一个联通块变色,然后和周围颜色相同的联通块组成大的联通块,求最少多少次能让所有联通块变为同色

联通块直接 DFS 缩点, 然后新图变为黑白相间的树。考虑树的直径有下列性质:

以树的直径中点上的一点为根,将这颗无根树转化为有根树的话,这样做会使树的深度最小,且为树的直径的一半(向上取整)(因为如果有一个叶子节点比直径更深的话,那么就可以构成一个更长的直径,与假设矛盾。)

所以答案是$(d+1)/2$, $d$为直径长。因为按照树的直径中点为根,每次操作直径中点改变颜色,改变$(d+1)/2$次就能改为同色。画图理解。

不要犯了老错误, CF 909E就误以为0点只有一层而错误。此题因为没有考虑到“一个联通块变色,然后和周围颜色相同的联通块组成大的联通块”,以为比较黑白联通块个数,造成了错误。

#include<cstdio> 
#include<cstring>
#include<algorithm>
#include<iostream>
#include<vector>
#include<queue>
#define ms(i, j) memset(i, j, sizeof i)
#define LL long long
#define db double
using namespace std;

const int MAXN = 200000 + 5;

vector<int> G[MAXN], G2[MAXN];
int n, col[MAXN], bl[MAXN], sz, vis[MAXN], dis[MAXN];
queue<int> q;

void ins(int x, int y) {G[x].push_back(y), G[y].push_back(x);}
void ins2(int x, int y) {G2[x].push_back(y), G2[y].push_back(x);}

void dfs(int u, int pa, int cha) {
    bl[u] = cha;
    for (int i = 0; i < (int)G[u].size(); i++) {
        int v = G[u][i];
        if (v != pa) {
            if (col[v] != col[u]) dfs(v, u, ++sz);
            else dfs(v, u, cha);
        }
    }
}

void clean() {
    ms(vis, 0), sz = 0;
}
int solve() {
    clean();
    for (int i = 1; i <= n; i++) scanf("%d", &col[i]);
    for (int x, y, i = 1; i < n; i++) scanf("%d%d", &x, &y), ins(x, y);
    dfs(1, 0, ++sz);
    for (int u = 1; u <= n; u++) {
        for (int i = 0; i < (int)G[u].size(); i++) {
            int v = G[u][i];
            ins2(bl[u], bl[v]);
        }
    }

    q.push(1), vis[1] = 1, dis[1] = 0;
    int mks = 0, pos = 1;
    while (!q.empty()) {
        int u = q.front(); q.pop();
        for (int i = 0; i < (int)G2[u].size(); i++) {
            int v = G2[u][i];
            if (!vis[v]) {
                vis[v] = 1, dis[v] = dis[u] + 1, q.push(v);
                if (dis[v] > mks) mks = dis[v], pos = v;
            }
        }
    }

    ms(vis, 0);
    q.push(pos), vis[pos] = 1, dis[pos] = 0, mks = 0;
    while (!q.empty()) {
        int u = q.front(); q.pop();
        for (int i = 0; i < (int)G2[u].size(); i++) {
            int v = G2[u][i];
            if (!vis[v]) {
                vis[v] = 1, dis[v] = dis[u] + 1, q.push(v);
                if (dis[v] > mks) mks = dis[v];
            }
        }
    }

    printf("%d\n", (mks + 1) / 2);
    return 0; 
}
int main() {
    scanf("%d", &n), solve();
    return 0;
}
------ 本文结束 ------