(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;
}