牛客练习赛 41 简单数学题(数论 + 状态压缩 + FWT)

牛客练习赛 41 简单数学题(数论 + 状态压缩 + FWT)_第1张图片

牛客练习赛 41 简单数学题(数论 + 状态压缩 + FWT)_第2张图片

牛客练习赛 41 简单数学题(数论 + 状态压缩 + FWT)_第3张图片

 

 

非常有意思的题,我解题的过程也是非常的精彩。纪念一下人生第一道FWT。

首先是第一个f(t)函数,根据它的定义,首先是x必须是t的因子,然后要求莫比乌斯函数不为0,而且要满足这个条件的最大值。根据这几个条件,显然x就是t分解质因子后,所有质因子的乘积。然后g(x)函数,就是看每个因子的指数,如果是奇数那么这个质因子还在,否则相当于没有这个质因子。最后就是F(a,b,c),它等于g(a*b*c)。转换一下,相当于对a、b和c所有质因子的并集的函数。我们考虑每一个质因子pi,它的相乘之后的指数为di=ai+bi+ci。可以看到,di的取值只有可能是0、1、2和3,且ai、bi和ci是0或者1。当di为偶数时相当于质因子最后的指数为0,为奇数的时候相当于指数为1。如果你观察仔细的话,我们可以发现这样一个关系:

                                                                 \large B_i=a_i \oplus b_i \oplus c_i

然后,题目保证了a、b和c的最大质因子都不会超过71,而71以内恰好只有20个质数。20个质数加上这个异或,很好的提示了我们需要用到状态压缩,也即用位运算来快速计算F(a,b,c)的数值。我们用一个20位的二进制,表示一个数字是否有对应序号的质数作为其质因子。然后异或就可以得到最后答案的所有质因子,可以反推答案。但是,我们最后要求的是对于数列A、B和C中,所有的a、b和c求出所有F(a,b,c)的数值与其出现次数的乘积和。这里三个数列的长度都是可以到1e5的级别。如果暴力求所有的数值,即使用上位运算可以O(1)计算一个函数值,总的复杂度还是会到O(N^3)。

由于我们是异或操作,而且位数只有20,所以我们不管怎么异或我们的结果不会超过2^20。所以我们大可以用一个多项式来表示这2^20个数字每个数字出现的次数。这样我们就可以联想到FFT,FFT对应两个多项式相乘,得到结果是两个多项式任取两项的组合的结果加到项数的和里面。但是这里我们要求的是,结果应该加到二者的异或结果里面。误打误撞,突然发现这个恰好变成了FWT的模板题了。

简单说一下FWT。

对于普通的FFT,我们是计算:\large C_k=\sum_{i+j=k}A_i*B_j

但是对于FWT,我们是计算:

                                                         \large C_k=\sum_{i|j=k}A_i*B_j

                                                         \large C_k=\sum_{i\&j=k}A_i*B_j

                                                         \large C_k=\sum_{i\oplus j=k}A_i*B_j

功能大概是这样的,至于原理就不在这里说了,直接上模板。

具体来说,对于输入的每个数列中的每个数字,直接进行质因数分解,然后根据出现的质因子构造20位的二进制数字,把对应数列的数字出现次数加一。处理完三个数列之后,就会有三个多项式,把三个多项式用FWT卷起来。最后用每一项的出现次数乘以每一项二进制数对应的数值求和即可。具体见代码:

#include 
#define LL long long
#define sc(x) scanf("%d",&x)
#define scc(x,y) scanf("%d%d",&x,&y)
#define sccc(x,y,z) scanf("%d%d%d",&x&y,&z)
using namespace std;

const int M = 1<<20;
const int N = 1e5 + 10;
const int mod = 1e9 + 7;
const int inv2 = 5e8 + 4;
const int p[20]={2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71};

int A[M<<1],B[M<<1],C[M<<1],n;

void FWT(int *a,int opt)
{
    for(int i=1;i1;j++)
        {
            if (x%p[j]) continue;
            y|=1<1;j++)
        {
            if (x%p[j]) continue;
            y|=1<1;j++)
        {
            if (x%p[j]) continue;
            y|=1<

 

 

 

 

 

你可能感兴趣的:(---------Online,Judge--------,牛客,数论,欧拉/莫比乌斯,FFT/NTT/FWT,bitset/状态压缩,牛客练习赛,数论,状态压缩,FWT)