牛客网多校9 Circulant Matrix (FWT)

24 篇文章 0 订阅
3 篇文章 0 订阅

题目:给你一个a数组和b数组,构造出A[i][j]矩阵(A[i][j] = a[i xor j]),解x数组。
n等于4的时候有:
A[0][0]*x[0] + A[0][1]*x[1] + A[0][2]*x[2] + A[0][3]*x[3] = b[0] (mod p)
A[1][0]*x[0] + A[1][1]*x[1] + A[1][2]*x[2] + A[1][3]*x[3] = b[1] (mod p)
A[2][0]*x[0] + A[2][1]*x[1] + A[2][2]*x[2] + A[2][3]*x[3] = b[2] (mod p)
A[3][0]*x[0] + A[3][1]*x[1] + A[3][2]*x[2] + A[3][3]*x[3] = b[3] (mod p)

把上面随便拿出几行写出来,会发现是有

a0*x0+a1*x1+a2*x2+a3*x3=b0

a1*x0+a0*x1+a3*x2+a2*x3=b1

a2*x0+a3*x1+a0*x2+a1*x3=b2

a3*x0+a2*x1+a1*x2+a0*x3=b3

然后会发现 a 数组下标 i 与相应位置上的 x 数组下标 j  的抑或值就是这一行的 b 数组下标k

FWT可以对于两个数组a和b,求出他们的位运算卷积c,使得c[k]=   对于所有的i和j 满足 i位运算j等于k    sigma a[i]*b[j]

那么就可以求x数组了。

#include <bits/stdc++.h>

using namespace std;
typedef long long ll;
const int maxn=262144+10;
const int mod=1e9+7;
ll qmod(ll x,ll p)
{
    ll ans=1;
    while(p)
    {
        if(p&1)
            ans=ans*x%mod;
        x=x*x%mod;
        p>>=1;
    }
    return ans;
}
ll rev;
void FWT(int a[],int n)
{
    for(int d=1;d<n;d<<=1)
        for(int m=d<<1,i=0;i<n;i+=m)
            for(int j=0;j<d;j++)
            {
                int x=a[i+j],y=a[i+j+d];
                a[i+j]=(x+y)%mod,a[i+j+d]=(x-y+mod)%mod;
            }
}

void UFWT(int a[],int n)
{
    for(int d=1;d<n;d<<=1)
        for(int m=d<<1,i=0;i<n;i+=m)
            for(int j=0;j<d;j++)
            {
                int x=a[i+j],y=a[i+j+d];
                a[i+j]=1LL*(x+y)*rev%mod,a[i+j+d]=(1LL*(x-y)*rev%mod+mod)%mod;
            }
}
void solve(int a[],int b[],int n)
{
    FWT(a,n);
    FWT(b,n);
    for(int i=0;i<n;i++)
        a[i]=1LL*qmod(a[i],mod-2)*b[i]%mod;
    UFWT(a,n);
}
int n,a[maxn],b[maxn];
int main()
{
    scanf("%d",&n);
    rev=qmod(2,mod-2);
    for(int i=0;i<n;i++)
        scanf("%d",&a[i]);
    for(int i=0;i<n;i++)
        scanf("%d",&b[i]);
    solve(a,b,n);
    for(int i=0;i<n;i++)
        printf("%d\n",a[i]);
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值