[PA 2016] 覆盖 / Pokrycia

定义 \(V=\{1,2,\dots,n\}\) 的所有简单无向图中最小点覆盖为 \(k\) 的图个数为 \(f(n,k)\).

\(T\) 组询问,每次给定 \(n\) 和 \(k\),求 \(f(n,k) \bmod 2\) 的值。

\(1 \leq T \leq 2^{14}\),\(1 \leq n < 2^{14}\),\(0 \leq k < n\)。


令 \(k \gets n - k\),转化成最大独立集。

不妨求出所有 \((n,k)\) 对应的答案,使用 bitset 存储即可。

考虑模 \(2\) 的性质,这很关键,我们考虑找到一些能够映射的情况。


假设对于点 \(1\) 和点 \(2\),考虑在 \(\{3,4,\dots,n\}\) 中的邻域 \(N(1)\) 与 \(N(2)\)。

显然,若 \(N(1)\) 与 \(N(2)\) 不同,我们可以通过交换 \((1,2)\) 的方式构造出另一个同构图。

这两个图的最大独立集个数一定相同,在模 \(2\) 意义下不会产生贡献。

据此我们知道,忽略 \((1,2)\) 连边的情况下,两者邻域相同。

若 \((1,2)\) 之间有边,这两个点必然等价且最多选择 \(1\) 个,将其合并成贡献为 \(1\) 的点。

否则这两个点的可选性必然相同,我们可以将其合并为一个贡献为 \(2\) 的点。

考虑这样合并的过程是要求两个点的贡献相同的,这点在有边的情况上具有体现。

我们不断合并两个贡献相同的点,最终可以得到一些贡献不同的点。

考虑每次钦定选择最小的点合并,这和原先的情况就可以构成双射了。

接下来我们就会得到若干个贡献为 \(2^x\) 的点,设贡献之和为 \(m > 0\)。

将方案数拆成两部分,将 \(n\) 个点合并到贡献和为 \(m\) 的方案数,以及合并后的图方案数。


首先观察合并后的方案数。在这种情况下,最大独立集为 \(k\) 的选择方式是唯一的。

同时,我们观察到点权是不同的 \(2^i\),也就是说贪心做最大独立集一定是对的。

举个例子,假设合并后有 \(1,2,4,8,16\) 五个点,我们尝试寻找其中的一些性质。

假设点 \(4\) 未选,显然对于 \(\{1,2\}\) 中的点,\(4\) 是否有边都会与其选择性无关。

所以,对于非最小贡献的点,其必然都会被选择。

也就是说,\(k=m\) 或 \(k=m-\text{lowbit}(m)\)。

考虑此时图的连边方案数。假设 \(k=m\),显然所有点均无连边,方案数为 \(1\)。

否则对于非 lowbit 的部分显然无内部连边,对于 lowbit 进行讨论。

假设除 lowbit 以外没有点,则 \(k=0\),显然不符合题意。

否则对于剩余的点必然会连至少一条边,方案数一定是奇数,贡献为 \(1\)。

总之,合并后的图方案数 \(f_{m,k}\) 可以表示为 \([k=m]+[k=m-\text{lowbit}(m)]\)。


接下来考虑 \(n\) 个点合并后贡献为 \(m\) 的方案数,令其为 \(g_{n,m}\)。

考虑不断加入贡献为 \(1\) 的点,显然每次都是尽量向上合并,直到不能合并或合并后不变。

显然这两者产生的贡献都是 \(1\),我们只需要算方案数即可。

假设情况是不能合并,也就是合并后达到了 lowbit,可以从 \(g_{n-1,m-1}\) 转移。

假设情况是合并后不变了,显然较小的位都没有了,且一个 lowbit 消失了。

也就是说,我们会从 \(g_{n-1,m+\text{lowbit}(m)-1}\) 转移得到。

直接转移即可,复杂度 \(O(V^2)\),可以通过。空间复杂度 \(O(\frac{V^2}{\omega})\)。

//Ad astra per aspera
#include<iostream>
#include<cstdio>
#include<bitset>
using namespace std;
const int N=1<<14;
bitset<N> f[N],ans[N];
int main(){
	f[0][0]=1;
	for(int i=1;i<N;i++){
		for(int j=1;j<=i;j++){
			f[i][j]=f[i-1][j-1]^f[i-1][j+(j&-j)-1];
			if(f[i][j]){
				ans[i][j]=!ans[i][j];
				ans[i][j-(j&-j)]=!ans[i][j-(j&-j)];
			}
		}
	}
	int T;
	scanf("%d",&T);
	while(T--){
		int n,k;
		scanf("%d %d",&n,&k);
		printf("%d\n",int(ans[n][n-k]));
	}
	return 0;
}
posted @ 2026-05-13 14:49  Oken喵~  阅读(48)  评论(0)    收藏  举报