[PA 2016] 覆盖 / Pokrycia 题解
[PA 2016] 覆盖 / Pokrycia 题解
好牛的一道题目!
首先考虑把最小点覆盖转化为最大独立集,即求出点数为 最大独立集为 的图有多少个,然后预处理出所有可能的 的答案,并 回答询问。
本题的关键显然是模数为 ,我们要尝试利用这一性质。
设 表示 的邻域,如果 ,即点 和 连向外部的点的集合不一样,那么交换 和 这两个点会形成一张新的图,且这两张图答案一样,所以这种情况下的答案一定是偶数,不用考虑。于是只需要计算 的情况。
我们按照 和 之间有没有连边分成两种情况:
- 和 之间有连边,那么这两个点间一定有个点不能选,因为点 和 是等价的,不妨设点 不能选,于是可以直接删去点 。
- 和 之间没有连边,可以直接把 和 合并成一个新的点,要么都选,要么都不选。
这样子两个点都可以变成一个点,我们给每个点赋一个权重,表示这个点是由多少个点合并起来的,初始都为 。我们按照这样的方式类似的处理 ,就会有很多权重为 的点,然后两个权重为 的点又可以继续合并,就可以一直合并下去了(每次合并的两个点要求权重相同,且领域也相同)。
最后会有若干个权重为 的幂的点,假设从小到大分别为 ,并设 。根据前面的性质,一定有 互不相同(要不然可以继续合并),每个点包含的所有点之间都没有连边(没边才能合并)。
我们设 表示 个点最后变成二进制状态为 的方案数有多少种,设 表示点数为 最大独立集为 的图的个数。把问题分成两个部分,一是求出 ,二是求出一个 能转移到哪些 。先看第二个部分。
现在已知一个二进制状态 ,我们要确定在这个图上任意连边,对于每个 ,有多少个图的最大独立集为 。
假设最终的独立集大小为 ,那么 的每个二进制位一定是 里面的,要不然这些点凑不出来。
假如现在给你了一张图和里面的连边,要求出最大独立集。因为每一个点权重都是 的幂,于是有个贪心的思路,即从大到小枚举点,如果能选这个点就选,这样一定是最优的。
我们设点 表示最大独立集中最大的没有被选择的点,最小的点为 :
-
,即所有点都被选择了,那么只有所有点之间都没有连边这一个图是合法的,此时 。
-
,即除了 都被选择了,那么 之间肯定不能有连边, 到 之间可以随意连边,但不能一条边都没有,所以方案数为 ,是奇数。此时 。
-
,这个时候在 和 之间连边不会影响最大独立集,所以可以制造一一对应,这部分的答案都是偶数。
为什么一定要选取 而不是任意一个 ,假设 且 和 间有连边,如果删去连边有可能变成了 不在最大独立集内, 在最大独立集内了,没有一一对应,但是选择最小的 就没有这种情况。
所以只有在 或 时图的数量为奇数,其余都是偶数。所以一个 只能转移到 和 。
接下来再看怎么求 。考虑在二进制状态 下新加入一个数可以怎么转移。此时新加入一个权重为 的点,如果 中有权重为 的点,那么可以合并或者删去这个点。如果选择合并,就继续看 中是否有权重为 的点,如果有就还是可以选择合并或者删去。一直做下去,直到 中没有权重重复的点,那么有两种结束情况,可能是合并到没法合并了,或者是合并到中途把点删去了。
设 ,考虑这个权重为 个点上一步是怎么来的:
- 是加入一个点一直合并而来的,有转移 。
- 是加入一个点合并到一半被删除了,那么上一步的 一定要有所有权重比 小的点才能合并过来,有转移 。
于是这道题就做完了,总的来说就是两个转移:
直接做即可,时间复杂度 。然后这题直接开 bool 数组空间要爆,可以使用 bitset。
#include <iostream>
#include <cstdio>
#include <bitset>
#define ll long long
using namespace std;
const int N = 1<<14;
bitset<N> f[N],g[N];
inline int rd()
{
char c;int f = 1;
while(!isdigit(c = getchar()))if(c=='-')f = -1;
int x = c-'0';
while(isdigit(c = getchar()))x = x*10+(c^48);
return x*f;
}
int main()
{
f[0][0] = 1;
for(int i = 1;i < N;i++)for(int j = 1;j <= i;j++)
{
int k = j&-j;
f[i][j] = f[i-1][j-1]^f[i-1][j+k-1];
if(f[i][j])g[i].flip(j),g[i].flip(j-k);
}
for(int t = rd();t--;)
{int n = rd(),m = rd();printf("%d\n",(int)g[n][n-m]);}
return 0;
}
最后,至于为什么要把最小点覆盖转化为最大独立集,原因是最小点覆盖在删去一个点的时候还会让答案 ,而且最大独立集看起来转移式子更好推一点,当然直接当成最小点覆盖做应该也是可以的。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现