「牛客NOIP冲刺模拟赛」随机生成树

题目摘要

\(n\) 个有颜色的节点,第 \(i\) 个点的颜色为 \(c_i\) ,现在要用这 \(n\) 个点构造一棵树,满足:

  1. 根节点是 \(1\)
  2. 任何一个节点 \(i\) 的父亲节点的编号是 \(i\) 的约数

求可能的树上最大同色联通块个数。
\(n\le 500000\)

解题思路

考虑这样一件事:
对于一个 \(n\) 个点,\(m\) 条边的森林,它的联通块个数为 \(n-m\)
那么我们把每个同色联通块单独拎出来,那么就形成了一个 \(n\) 个点的森林,使联通块最多就是要让边数最少,也就是断掉的边最多。
一条边被断掉当且仅当他的两个端点颜色不同。
那么我们就尽量让每个节点连上一个和他颜色不同的父亲,然后用这些点统计答案就好了。

细节注意事项

  • \(O(n\sqrt{n})\) 可能会有点卡,枚举倍数可以做到 \(O(n\ln n)\)

参考代码

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cctype>
#include <cmath>
#include <ctime>
#define rg register
#define file(x) freopen(x".in", "r", stdin), freopen(x".out", "w", stdout)
using namespace std;
template < typename T > inline void read(T& s) {
    s = 0; int f = 0; char c = getchar();
    while (!isdigit(c)) f |= c == '-', c = getchar();
    while (isdigit(c)) s = s * 10 + (c ^ 48), c = getchar();
    s = f ? -s : s;
}

const int _ = 500002;

int n, c[_], a[_];

int main() {
//  file("randomtree");
    read(n);
    for (rg int i = 1; i <= n; ++i) read(c[i]);
    int ans = 1;
    for (rg int i = 1; i <= n; ++i)
        for (rg int j = 2; j * i <= n; ++j)
            a[j * i] |= (c[j * i] != c[i]);
    for (rg int i = 2; i <= n; i++) ans += a[i];
    printf("%d\n", ans);
    return 0;
} 

完结撒花 \(qwq\)

猜你喜欢

转载自www.cnblogs.com/zsbzsb/p/11759647.html