Loading

题解 【CF1227G Not Same】

\(\Large\texttt{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\n", a + 1);
    for (int i = 1; i <= a + 1; ++i, puts("")) rep(j, 1, a) printf("%d", ans[i][j]);
    return 0;
}
posted @ 2021-07-13 18:43  RedreamMer  阅读(75)  评论(0编辑  收藏  举报