「解题报告」ARC141D Non-divisible Set

很有意思的题,我又没想到咋做。

值域为 \(2m\),我们要找出一个大小为 \(m\) 的好集合,我们可以先来分析这个好集合的大小的上界是多少。

我们可以猜测一波上界就是 \(m\)。可以考虑把最小的质因子 \(2\) 拉出来,这样我们可以把每个数写成 \(a_i=p_i \times 2^{q_i}\) 的形式(\(p_i\) 为奇数)。那么我们发现,对于每一个 \(p\) 来说,\(p \times 2 ^ j\) 最多选出一个(否则就存在倍数),而 \(2m\) 中的奇数有 \(m\) 个,所以答案上界为 \(m\)

这启发我们对于每一个 \(k\) 来考虑它对应的 \(i\),我们把它记作 \(w_k\)。那么考虑 \(p_i\) 不相等的情况,如果 \(a_i | a_j\),那么 \(p_i | p_j\)\(q_i \le q_j\)。那么如果我们想要两两不是倍数,我们只需要让 \(\forall i | j, i \ne j, w_i > w_j\) 即可。

那么我们可以对每个 \(k\) 求出 \(w_k\) 的最小值 \(L_k\) 和最大值 \(R_k\),那么如果 \(q_i \in [L_{p_i}, R_{p_i}]\),就说明它可以被选。

如果要输出方案就是让左面的数都取到上边界,右面的数都取到下边界,这样中间这个数一定满足。

啥都想不到我太菜了啥都想不到我太菜了啥都想不到我太菜了啥都想不到我太菜了啥都想不到我太菜了啥都想不到我太菜了啥都想不到我太菜了啥都想不到我太菜了

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 600005;
int n, m;
int a[MAXN];
vector<int> b[MAXN];
int l[MAXN], r[MAXN];
vector<int> fac[MAXN], mul[MAXN];
int p[MAXN], q[MAXN];
int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
    }
    for (int i = 1; i <= n; i++) {
        int k = 0;
        int w = a[i];
        while (w % 2 == 0) k++, w /= 2;
        b[w].push_back(k);
        p[i] = w, q[i] = k;
    }
    for (int i = 1; i <= 2 * m; i += 2) {
        for (int j = 3; i * j <= 2 * m; j += 2) {
            fac[i * j].push_back(i);
            mul[i].push_back(i * j);
        }
    }
    memset(l, 0x3f, sizeof l);
    for (int i = 2 * m - 1; i >= 1; i -= 2) if (b[i].size()) {
        int L = 0;
        for (int w : mul[i]) {
            L = max(L, l[w] + 1);
        }
        for (int k : b[i]) {
            if (k >= L) {
                l[i] = k;
                break;
            }
        }
    }
    for (int i = 1; i <= 2 * m; i += 2) if (b[i].size()) {
        int R = INT_MAX;
        for (int w : fac[i]) {
            R = min(R, r[w] - 1);
        }
        for (int k : b[i]) {
            if (k <= R) {
                r[i] = k;
            }
        }
    }
    for (int i = 1; i <= 2 * m; i += 2) {
        if (l[i] > r[i]) {
            for (int j = 1; j <= n; j++) {
                printf("No\n");
            }
            return 0;
        }
    }
    for (int i = 1; i <= n; i++) {
        if (l[p[i]] <= q[i] && q[i] <= r[p[i]]) {
            printf("Yes\n");
        } else {
            printf("No\n");
        }
    }
    return 0;
}
posted @ 2023-01-19 23:06  APJifengc  阅读(37)  评论(0编辑  收藏  举报