1 条题解

  • 0
    @ 2025-8-24 23:08:50

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar max0810
    刚刚上初一,正在学习数据结构,求大佬带

    搬运于2025-08-24 23:08:50,当前版本为作者最后更新于2025-02-05 15:43:38,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    本文同步发表于 cnblogs

    好牛的一道题目!


    首先考虑把最小点覆盖转化为最大独立集,即求出点数为 nn 最大独立集为 nmn-m 的图有多少个,然后预处理出所有可能的 (n,m)(n,m) 的答案,并 O(1)O(1) 回答询问。

    本题的关键显然是模数为 22,我们要尝试利用这一性质。

    N(x)N(x) 表示 xx 的邻域,如果 N(1)\2N(2)\1N(1) \backslash 2\ne N(2)\backslash1,即点 1122 连向外部的点的集合不一样,那么交换 1122 这两个点会形成一张新的图,且这两张图答案一样,所以这种情况下的答案一定是偶数,不用考虑。于是只需要计算 N(1)\2=N(2)\1N(1) \backslash 2= N(2)\backslash1 的情况。

    我们按照 1122 之间有没有连边分成两种情况:

    • 1122 之间有连边,那么这两个点间一定有个点不能选,因为点 1122 是等价的,不妨设点 11 不能选,于是可以直接删去点 11
    • 1122 之间没有连边,可以直接把 1122 合并成一个新的点,要么都选,要么都不选。

    这样子两个点都可以变成一个点,我们给每个点赋一个权重,表示这个点是由多少个点合并起来的,初始都为 11。我们按照这样的方式类似的处理 (3,4),(5,6)(3,4),(5,6)\ldots,就会有很多权重为 22 的点,然后两个权重为 22 的点又可以继续合并,就可以一直合并下去了(每次合并的两个点要求权重相同,且领域也相同)。

    最后会有若干个权重为 22 的幂的点,假设从小到大分别为 2a1,2a22al2^{a_1},2^{a_2}\ldots 2^{a_l},并设 m=2aim = \sum2^{a_i}。根据前面的性质,一定有 aia_i 互不相同(要不然可以继续合并),每个点包含的所有点之间都没有连边(没边才能合并)。

    我们设 f(n,m)f(n,m) 表示 nn 个点最后变成二进制状态为 mm 的方案数有多少种,设 g(n,m)g(n,m) 表示点数为 nn 最大独立集为 mm 的图的个数。把问题分成两个部分,一是求出 f(n,m)f(n,m),二是求出一个 f(n,m)f(n,m) 能转移到哪些 g(n,k)g(n,k)。先看第二个部分。

    现在已知一个二进制状态 mm,我们要确定在这个图上任意连边,对于每个 kk,有多少个图的最大独立集为 kk

    假设最终的独立集大小为 kk,那么 kk 的每个二进制位一定是 aia_i 里面的,要不然这些点凑不出来。

    假如现在给你了一张图和里面的连边,要求出最大独立集。因为每一个点权重都是 22 的幂,于是有个贪心的思路,即从大到小枚举点,如果能选这个点就选,这样一定是最优的。

    我们设点 xx 表示最大独立集中最大的没有被选择的点,最小的点为 a1a_1

    • x=0x=0,即所有点都被选择了,那么只有所有点之间都没有连边这一个图是合法的,此时 k=mk=m

    • x=a1x=a_1,即除了 a1a_1 都被选择了,那么 a2ala_2\ldots a_l 之间肯定不能有连边,a1a_1a2ala_2\ldots a_l 之间可以随意连边,但不能一条边都没有,所以方案数为 2l112^{l-1}-1,是奇数。此时 k=mlowbit(m)k=m-\operatorname{lowbit}(m)

    • x>a1x > a_1,这个时候在 xxa1a_1 之间连边不会影响最大独立集,所以可以制造一一对应,这部分的答案都是偶数。

      为什么一定要选取 a1a_1 而不是任意一个 aia_i,假设 ai<xa_i < xxxaia_i 间有连边,如果删去连边有可能变成了 aia_i 不在最大独立集内,xx 在最大独立集内了,没有一一对应,但是选择最小的 a1a_1 就没有这种情况。

    所以只有在 k=mk=mk=mlowbit(m)k=m-\operatorname{lowbit}(m) 时图的数量为奇数,其余都是偶数。所以一个 f(n,m)f(n,m) 只能转移到 g(n,m)g(n,m)g(n,mlowbit(m))g(n,m-\operatorname{lowbit}(m))

    接下来再看怎么求 f(n,m)f(n,m)。考虑在二进制状态 mm 下新加入一个数可以怎么转移。此时新加入一个权重为 11 的点,如果 mm 中有权重为 11 的点,那么可以合并或者删去这个点。如果选择合并,就继续看 mm 中是否有权重为 22 的点,如果有就还是可以选择合并或者删去。一直做下去,直到 mm 中没有权重重复的点,那么有两种结束情况,可能是合并到没法合并了,或者是合并到中途把点删去了。

    x=lowbit(m)x=\operatorname{lowbit}(m),考虑这个权重为 xx 个点上一步是怎么来的:

    • 是加入一个点一直合并而来的,有转移 f(n,m)f(n1,m1)f(n,m)\larr f(n-1,m-1)
    • 是加入一个点合并到一半被删除了,那么上一步的 mm 一定要有所有权重比 xx 小的点才能合并过来,有转移 f(n,m)f(n1,m+x1)f(n,m)\larr f(n-1,m+x-1)

    于是这道题就做完了,总的来说就是两个转移:

    $$f(n,m) = f(n-1,m-1)+f(n-1,m+\operatorname{lowbit}(m)-1) \\ f(n,m)\rarr g(n,m),f(n,m)\rarr g(n,m-\operatorname{lowbit}(m)) $$

    直接做即可,时间复杂度 O(n2)\mathcal{O}(n^2)。然后这题直接开 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;
    }
    

    最后,至于为什么要把最小点覆盖转化为最大独立集,原因是最小点覆盖在删去一个点的时候还会让答案 +1+1,而且最大独立集看起来转移式子更好推一点,当然直接当成最小点覆盖做应该也是可以的。

    • 1

    信息

    ID
    11319
    时间
    7000ms
    内存
    512MiB
    难度
    7
    标签
    递交数
    0
    已通过
    0
    上传者