4934 礼物
题意:$n$个$[0,2^k)$的数,分最少的组使得对于每个组的任意$a_1,a_2$, $a_1 \text{and} a_2 < \min(a_i,a_2)$,输出最少分组和一组方案。
首先将$a_1 \text{and} a_2 < \min(a_i,a_2)$转化,我们发现$a_1 \text{and} a_2$是不可能大于$\min(a_i,a_2)$的,所以这里就相当于只有等于的情况不成立。进一步分析可得,对于每个组的任意$a_1,a_2$,都不存在$a_1 \subseteq a_2$。
先介绍偏序:
偏序的定义
设$R$是集合$A$上的一个二元关系,若R满足:
1、自反性:对任意$x∈A$,有$xRx$;
2、反对称性(即反对称关系):对任意$x,y∈A$,若$xRy$,且$yRx$,则$x=y$;
3、传递性:对任意$x, y,z∈A$,若$xRy$,且$yRz$,则$xRz$。
则称$R$为$A$上的偏序关系。
符号$≤$
具有偏序关系的集合$P$为偏序集,对应一个DAG图
严格偏序的定义
1、非自反性:$x ≮ x$
2、非对称性:若$x < y$, 则$y ≮ x$
3、传递性:若$x < y, y < z$, 则$x < y$
严格偏序也对应一个DAG图。但是不能使用Dilworth定理
Dilworth定理
在DAG图中
1、最小反链覆盖等于最长链长度等于拓扑序层次数:拓扑排序
2、最小链覆盖等于最长反链长度等于链的起点数:二分图
反链:在DAG图中不存在任意两个点是前驱后继关系的点集
将$a \subseteq b$看作一个偏序关系(题目保证$a_i$互不相同,则为集合),然后建立一个 DAG 图,根据Dilworth定理,我们可以发现这里就是在求一个最小反链覆盖,那么求图的最长链即为答案。
注意本题的连边优化,若$u$到$v$有一条边,$v$到$w$有一条边,那么$u$到$w$的边是无意义的。所以我们每次连边只用连与当前状态二进制下1相差一位的。因为这样连就不会连出上面的$u$到$w$的边,并且将边的数量转到了$k \cdot 2^n$级别。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<cmath>
#include<vector>
#include<queue>
#define ms(i, j) memset(i, j, sizeof i)
#define LL long long
#define db double
#define fir first
#define sec second
#define mp make_pair
using namespace std;
namespace flyinthesky {
const int MAXN = 1100000 + 5;
int n, k, vd[MAXN], dp[MAXN], ino[MAXN];
vector<int > G[MAXN], vec[MAXN];
queue<int > q;
void clean() {
ms(vd, 0), ms(dp, 0), ms(ino, 0);
}
int solve() {
clean();
cin >> n >> k;
for (int x, i = 1; i <= n; ++i) scanf("%d", &x), vd[x] = 1;
for (int S = 0; S < (1 << k); ++S)
for (int i = 0; i < k; ++i) if ((S & (1 << i)) == 0) G[S].push_back(S ^ (1 << i)), ++ino[S ^ (1 << i)];
q.push(0);
while (!q.empty()) {
int u = q.front(); q.pop();
if (vd[u]) ++dp[u];
for (int i = 0; i < (int)G[u].size(); ++i) {
int v = G[u][i];
ino[v]--;
dp[v] = max(dp[v], dp[u]);
if (ino[v] == 0) q.push(v);
}
}
for (int u = 0; u < (1 << k); ++u) {
if (!vd[u]) continue ;
vec[dp[u]].push_back(u);
}
int sz = 0;
for (int i = 0; i <= dp[(1 << k) - 1]; ++i) if ((int)vec[i].size() > 0) ++sz;
printf("1\n%d\n", sz);
for (int u = 0; u <= dp[(1 << k) - 1]; ++u) {
if ((int)vec[u].size() <= 0) continue ;
printf("%d ", (int)vec[u].size());
for (int i = 0; i < (int)vec[u].size(); ++i)
printf("%d ", vec[u][i]);
printf("\n");
}
return 0;
}
}
int main() {
flyinthesky::solve();
return 0;
}