专栏文章
题解:P12371 【模板】最大团/最大独立集
P12371题解参与者 2已保存评论 1
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 1 条
- 当前快照
- 1 份
- 快照标识符
- @mipf98zr
- 此快照首次捕获于
- 2025/12/03 11:02 3 个月前
- 此快照最后确认于
- 2025/12/03 11:02 3 个月前
一个比较好写的时空 做法。
考虑每次去掉一个点搜索。我们希望记录一部分结果以减小复杂度,因此考虑每次去掉编号最大的那个点,并记忆化所有只使用编号最小的 个点的情况。
这里要记下的结果包含:大小,个数,和一个例子。只要能比较与合并一个点进去,就可以这样做。
下面考虑的是独立集(这样好写些)。
记当前集合为 ,要去掉的也是编号最大的点为 , 的领域为 。则,选 入独立集,转化在 上考虑原问题,并将 加入结果;否则,转化为在 上考虑原问题。
考虑复杂度证明。前 步只会拓展出 个状态,而后 步涉及的总状态数不超过 ,由于记忆化每种只会被访问一次,因此这个做法的时空复杂度均为 。
这个复杂度证明方式跟 CF1336E1 很像。
因为是独立集,要先对反图求解,再对原图求解。可以把开始的 就看成补集,但那样代码可读性会下降,我就没写。
CPP#include <bits/stdc++.h>
using LL = long long;
struct node {
int mx, cnt; LL ex;
node(int mx = 0, int cnt = 1, LL ex = 0) : mx(mx), cnt(cnt), ex(ex) {}
node operator + (const node &t) const {
if (mx != t.mx) return mx > t.mx ? *this : t;
return {mx, cnt + t.cnt, ex};
}
node operator + (int x) const {
return {mx + 1, cnt, ex | (1ll << x)};
}
};
int main() {
int n, m; scanf("%d %d", &n, &m);
std::vector<LL> e(n);
for (int i = 0, u, v; i < m; i++) {
scanf("%d %d", &u, &v), --u, --v;
e[u] |= 1ll << v, e[v] |= 1ll << u;
}
LL lim = 1ll << (n / 2), all = (1ll << n) - 1;
std::vector<node> f(lim);
std::function<node(LL)> dfs = [&](LL st) {
if (st < lim && f[st].mx > 0) return f[st];
if (!st) return node();
int x = std::__lg(st);
LL r = st & (all ^ e[x] ^ (1ll << x));
node ans = dfs(st ^ (1ll << x)) + (dfs(r) + x);
if (st < lim) f[st] = ans;
return ans;
};
for (int i = 0; i < n; i++)
e[i] = all ^ (1ll << i) ^ e[i];
node ans = dfs(all);
printf("%d %d\n", ans.mx, ans.cnt);
for (int i = 0; i < n; i++) if (ans.ex & (1ll << i))
printf("%d ", i + 1);
printf("\n");
for (int i = 0; i < n; i++)
e[i] = all ^ (1ll << i) ^ e[i];
f.assign(lim, node());
ans = dfs(all);
printf("%d %d\n", ans.mx, ans.cnt);
for (int i = 0; i < n; i++) if (ans.ex & (1ll << i))
printf("%d ", i + 1);
printf("\n");
}
相关推荐
评论
共 1 条评论,欢迎与作者交流。
正在加载评论...