专栏文章
题解:P4934 礼物
P4934题解参与者 2已保存评论 1
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 1 条
- 当前快照
- 1 份
- 快照标识符
- @miqn48dm
- 此快照首次捕获于
- 2025/12/04 07:30 3 个月前
- 此快照最后确认于
- 2025/12/04 07:30 3 个月前
解题思路
首先我们可以发现,假设 ,那么 当且仅当 。显然地,如果 ,那么 就不能与 存在于同一个箱子,其中 是 的子集。
考虑拓扑排序,每个数字 向所有比它小且不能与它共存的数字 连边,并且置 ,其中 表示数字 放在哪一个箱子中。
但是这样每个数字连出边的量级是 的,显然在后面几组数据中不可行。于是考虑每个数字只向二进制下只比它少一位 且不能与它共存的数字连边,无论目标数字是否存在,可以证明那些被忽略的边必然会被间接连接。这样每个数字连出边的量级只有 ,可以通过。
另外,注意使用这种方式连边需要为 中的每一个数字而不仅仅是输入的数字连边。拓扑排序时,如果 不属于输入,则应该更新 为 ,而不是 。
参考代码
CPP#include <bits/stdc++.h>
using namespace std;
int n, k, a[1000024], r[1050024], in[1050024], msv[1050024];
int ev[22000024], ex[22000024], es[1050024], ecnt;
vector<int> ans[24];
void add(int u, int v) {
ev[++ecnt] = v;
ex[ecnt] = es[u];
es[u] = ecnt;
in[v]++;
}
#define lowbit(x) ((x) & -(x))
int main() {
scanf("%d %d", &n, &k);
for (int i = 1; i <= n; i++) {
scanf("%d", a + i);
r[a[i]] = i;
}
sort(a + 1, a + n + 1);
for (int i = 0; i < (1 << k); i++) {
int tmp = i;
while (tmp) {
int x = lowbit(tmp);
add(i, i - x);
tmp -= x;
}
}
queue<int> q;
q.push((1 << k) - 1);
while (!q.empty()) {
int u = q.front();
q.pop();
int t = r[u];
if (t) ans[msv[u] + 1].push_back(u);
for (int i = es[u]; i; i = ex[i]) {
int v = ev[i];
if (t) msv[v] = max(msv[v], msv[u] + 1);
else msv[v] = max(msv[v], msv[u]);
if (!--in[v]) q.push(v);
}
}
int x = 1;
for (; ans[x].size(); x++);
x--;
printf("1\n%d\n", x);
for (int i = 1; i <= x; i++) {
printf("%d", ans[i].size());
for (auto u : ans[i]) {
printf(" %d", u);
}
puts("");
}
return 0;
}
相关推荐
评论
共 1 条评论,欢迎与作者交流。
正在加载评论...