数学、bitset

能和每个 $i$ 相邻的数可以 $O(n^2)$ 预处理得到,二进制表示中 $1$ 的数量可以用 __builtin_popcount 快速求,接下来对于每个 $p$ 讨论解法。

$$\bf{p=1}$$

输出 $n$,时间复杂度 $O(1)$。

$$\bf{p=2}$$

枚举所有数对,时间复杂度 $O(n^2)$。

$$\bf{p=3}$$

使用 $w_i$ 表示能和 $i$ 相邻的数的数量,使用 bitset 以 $O \left( \dfrac{n}{\omega} \right)$ 时间复杂度求出 $w_i$,时间复杂度 $O(n^2)$,答案如下式。

$$\sum\limits_{i=1}^{n} (w_i - 1) w_i$$

$$\bf{p=4}$$

首先预处理能和 $i$ 相邻的数,用 bitset 存起来,而 $w_i$ 直接调用 bitset.count() 求就可以了。

枚举 $4$ 元组的第 $2,3$ 个数,使用 $i,j$ 表示这俩数。用两个 bitset 与一下得到能和它俩相邻的数的数量 $x$,它们对答案的贡献为 $(w_i-1) \times (w_j-1) - x$,时间复杂度 $O \left( \dfrac{n^3}{\omega} \right)$。

$$\bf{p=5}$$

故技重施,枚举 $5$ 元组的第 $2,4$ 个数,还是使用 $i,j$ 表示这俩数。用两个 bitset 与出可选的第 $3$ 个数的数量 $x$,应该可以容斥掉重复贡献得到最终贡献。但是我没想明白咋容斥,所以直接分讨了。

显然第 $3$ 个数必须同时能和 $i,j$ 挨着,这种数有 $x$ 个。只能和第 $i$ 个数挨着的数有 $w_i - x$ 个,只能和第 $j$ 个数挨着的数有 $w_j - x$ 个。记得处理 $i,j$ 能够相邻的情况,如果它们能相邻则修改 $i,j$ 对应的 bitset $s_i,s_j$ 使得 $s_{i,j}=0,s_{j,i}=0$ 并在计算后复原,否则会由于 $w_i,w_j$ 的值错误而多算,因为此时 $i$ 不能作第 $5$ 个数,$j$ 不能作第 $1$ 个数。

$1$ 只能挨着 $2$$1$ 能挨着 $2,4$
$5$ 只能挨着 $4$$x(w_i - x)(w_j - x)$$x(x - 1)(w_j - x)$
$5$ 能挨着 $2,4$$x(w_i - x)(x - 1)$$x (x-1) (x - 2)$

时间复杂度 $O \left( \dfrac{n^3}{\omega} \right)$。

#include<bits/stdc++.h>
using namespace std;
constexpr int mod=998'244'353;
function<int()> solution[6];
bitset<1'005> s[1'005];
int n,k,p;
int main(){
    solution[1]=[&](){return n;};
    solution[2]=[&](){
        int ret=0;
        for(int i=1;i<=n;i++) ret+=s[i].count();
        return ret;
    };
    solution[3]=[&](){
        int ret=0;
        for(int i=1;i<=n;i++) ret+=(s[i].count()-1)*s[i].count();
        return ret;
    };
    solution[4]=[&](){
        int ret=0;
        for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(s[i][j])
            ret=(ret+(s[i].count()-1)*(s[j].count()-1)-(s[i]&s[j]).count())%mod;
        ret=(ret+mod)%mod;
        return ret;
    };
    solution[5]=[&](){
        int ret=0;
        for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) if(i!=j){
            if(s[i][j]) s[i][j]=0,s[j][i]=0;
            int x=(s[i]&s[j]).count();
            ret=(ret+1ll*x*(s[i].count()-x)*(s[j].count()-x))%mod;
            ret=(ret+1ll*x*(x-1)*(s[j].count()-x))%mod;
            ret=(ret+1ll*x*(s[i].count()-x)*(x-1))%mod;
            ret=(ret+1ll*x*(x-1)*(x-2))%mod;
            if(__builtin_popcount(i^j)==k) s[i][j]=1,s[j][i]=1;
        }
        ret=(ret+mod)%mod;
        return ret;
    };
    scanf("%d%d%d",&n,&k,&p);
    for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) s[i][j]=(__builtin_popcount(i^j)==k);
    printf("%d\n",solution[p]());
    return 0;
}
最后修改:2024 年 09 月 24 日
如果觉得我的文章对你有用,请随意赞赏