题解 【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;
}