HDU 6057 Kanade's convolution

HDU 6057 Kanade’s convolution

原题连接:
http://acm.hdu.edu.cn/showproblem.php?pid=6057

对于这一题
计算:

Ck=i and j=kAi xor jBi or j

令:
and 可以理解为交集, or 可以理解为并集, xor 可以理解为交集在并集中的补集。
这里的集合是指, 2 进制形式,每一位取或者不取的情况。

1为取,0为不取。

下面为们把所有整数看作二进制集合。逻辑运算分别对应相应对,集合运算。

xor 并-交

or  
and
x=i xor j , y=i or j
由上文并交补的关系有下面等价关系:

i and j=k <>x and y=x  yx=k

对于给定 x , y, 有多少满足 x y的有序对 (i,j)
因为 , i and j=k   , x=i xor j , y=i or j
所以 x and k=0 x or k=y
这也就是说。 x k对立
x 是由xor得来。
x 中对元素要么属于i要么属于 j ,不同时成立

x中元素数量为 bit(x) , 那么满足, x,y 关系的有序对数量为:

i=0(bit(x)i)=2bit(x)

所以:

Ck=xy[x and y=x][yx=k]2bit(x)AxBy

因为 x ,k对立。所以: x xor y=k

Ck=x xor y=k[yx=k]2bit(x)AxBy

因为 k=x xor y 时。
当且仅当 bit(y)bit(x)=bit(k) 时, xk 对立。
所以:

Ck=x xor y=k[bit(y)bit(x)=bit(k)]2bit(x)AxBy

定义一种数列运算 F() 有:
F(A,k)
并且有:
F(A,k)i=[bit(i)=k]Ai

上面的操作其实是吧 一个数列拆成了若干序列。

按照下标二进制 1 的数量拆分。

通过 FWT 得到类似 FFT 的点值表达
既然是点值表达,那么 加 减 乘 就很随意了。
然后通过 FWT(F(A,k)),FWT(F(B,k)) 得到 FWT(F(C,k))
(通过点值表达 进行 乘法,加法)
则:

Ck=F(C,bit(k))k

#include <stdio.h>
#include <string.h>
#include <algorithm>
#define MAXN 600000
using namespace std;
typedef long long LL;
const LL mod =998244353;

LL Pow(LL a,LL b)
{
    LL tmp=1;
    a%=mod;
    while(b)
    {
        if(b&1)
            tmp=tmp*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return tmp;
}

const LL I2=Pow(2,mod-2);

void FWT(LL y[],int l,int r)
{
    if(l==r)return;
    int mid=1+((l+r)>>1);
    FWT(y,l,mid-1);
    FWT(y,mid,r);
    for(int i=l,j=mid;i<mid;i++,j++)
    {
        LL u=y[i];
        LL t=y[j];
        y[i]=u+t;   if(y[i]>=mod)y[i]-=mod;
        y[j]=u-t;   if(y[j]<0) y[j]+=mod;
    }
}

void IFWT(LL y[],int l,int r)
{
    if(l==r)return ;
    int mid=1+((l+r)>>1);
    for(int i=l,j=mid;i<mid;i++,j++)
    {
        LL u=y[i];
        LL t=y[j];
        y[i]=(u+t)*I2%mod;
        y[j]=(u-t+mod)*I2%mod;
    }
    IFWT(y,l,mid-1);
    IFWT(y,mid,r);
}

LL bit[MAXN];
LL A[23][MAXN];
LL B[23][MAXN];
LL C[23][MAXN];
LL ds[25];
int main ()
{
    LL a;
    int m;
    scanf("%d",&m);
    int len=1<<(m++);
    for(int i=0;i<len;i++) bit[i]=bit[i>>1]+(i&1);

    for(int i=0;i<len;i++)
    {
        scanf("%lld",&a);
        A[bit[i]][i]=(a*(1<<bit[i]))%mod;
    }

    for(int i=0;i<len;i++)  scanf("%lld",B[bit[i]]+i);
    for(int i=0;i<m;i++) FWT(A[i],0,len-1);
    for(int i=0;i<m;i++) FWT(B[i],0,len-1);

    for(int i=0;i<m;i++)
        for(int j=i;j<m;j++)
            for(int t=0;t<len;t++)
                C[j-i][t]=(C[j-i][t]+A[i][t]*B[j][t])%mod;


    for(int i=0;i<m;i++) IFWT(C[i],0,len-1);

    LL ans=0,base=1;

    for(int i=0;i<len;i++)
    {
        ans=(ans+C[bit[i]][i]*base)%mod;
        base=base*1526%mod;
    }

    printf("%lld\n",ans);

    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值