• 周日. 6月 26th, 2022

5G编程聚合网

5G时代下一个聚合的编程学习网

热门标签

题解 CF1227G 【Not Same】

admin

11月 28, 2021

(Large exttt{CF1227G Not Same
})

标签:构造

疑似水2600?

题意

给定大小为 (n) 的序列 (a) , 满足 (1 le a_i le n)

你需要执行至多 (n+1) 次操作使得所有数变为 (0),每次操作你可以把一个子集的元素都 (-1),要求每次操作的子集互不相同。

(n le 1000)

思路

好像和题解区的构造方法都不太相同,但是很好理解。

首先假设最后序列有 (n + 1) 个。

因为最后的操作序列没有两两相同,我们从序列的角度来看,一开始是都相同的,我们认为它们都是在同一个集合中。

(以下“序列”即为集合中的元素)

操作即为每次由第 (i) 个数字 (a_i),给 (a_i) 个序列的末尾加上 (1),并给剩下 (n + 1 – a_i) 个序列末尾加上 (0),注意到这一步可以使一些相同的序列变得不同,即使同一个集合中的元素通过在末尾添加了不同的 bit 变成了不相同,这时候就可以把加不同 bit 的这些元素拉出来变成一个新的集合(比如加 (0) 的不动,放 (1) 的新拉出来一个集合)。

然后我们注意到 (1 le a_i le n) 这说明每个 (a_i) 对序列的操作至少有一个 (0),至少有一个 (1)

也就是说每个数如果操作分配的好,一定至少有一个集合大小 (ge 2) 的集合可以被分成两份,又因为初始集合大小为 (n + 1)(n) 次一定分得 (n + 1) 个不同的集合。

代码

int a, s[N + 5], ans[N + 5][N + 5], top = 1;
vector<int> q[N + 5];

void add(int n, int j) {
    rep(i, 0, siz(q[n]) - 1) ans[q[n][i]][j] = 1;
}

void divide(int n, int p0, int p1, int j) {
    ++top;
    rep(i, 1, p0) q[top].PB(q[n][siz(q[n]) - 1]), q[n].pop_back();
    add(n, j);
}

signed main() {
    // freopen("in1.in", "r", stdin);
    // freopen("out.out", "w", stdout);
    a = read();
    rep(i, 1, a) s[i] = read(), q[1].PB(i);
    q[1].PB(a + 1);
    rep(i, 1, a) {
        int p0 = a + 1 - s[i], p1 = s[i];
        int _top = top;
        rep(j, 1, _top) {
            if (siz(q[j]) == 1) {
                if (p1 > p0) add(j, i), --p1;
                else --p0;
            }
            else if (!p0) {
                p1 -= siz(q[j]);
                add(j, i);
            }
            else if (!p1) p0 -= siz(q[j]);
            else {
                int q0 = min(p0, siz(q[j]) - 1), q1 = siz(q[j]) - q0;
                divide(j, q0, q1, i);
                p0 -= q0;
                p1 -= q1;
            }
        }
    }
    printf("%d
", a + 1);
    for (int i = 1; i <= a + 1; ++i, puts("")) rep(j, 1, a) printf("%d", ans[i][j]);
    return 0;
}

发表评论

您的电子邮箱地址不会被公开。