【HNOI2016模擬4.4】Fenwit

Description

這裏寫圖片描述

Solution

雖然這題還有很多的方法,但是這類型的題目鬥遊戲一個通用的做法FWT(快速沃爾什變換),而且非常的短。
首先我們可以把式子轉化成:

Fi+1[jk]=k=02M1Fi[k]B[Cnt(j)]

根據異或的性質,這個很顯然。
然後就用後面的兩個數組做FWT,就可以了。
但是還要注意一個問題,就是這個模數可能的2沒有逆元,FWT的時候是要除以2的。
我們探究一下,要使
A2mB(modC) (FWT有m層)因爲有mod,B< C
根據模的性質,這個有
AB2m(modC2m)
此時A就不用除了,然後給C乘上2m 次方,最後再用B除上2m
還有一個優化,我們可以發現T十分的大,但是還要快速冪。
其實我們可以發現擴展歐拉定理在矩陣乘法上也適用,所以我們可以先把T的phi求出來,然後再搞一搞。

Code

#include<iostream>
#include<stdio.h>
#include<string.h>
#include<math.h>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=3e5+7;
typedef long long ll;
ll i,j,k,l,t,n,m,ans,mo,T,ni2,pp,ui;
ll a[maxn],b[maxn],er[20],c[maxn];
bool bz;
char ch;
ll phi(ll x){
    ll i,j=x;
    for(i=2;i<=x;i++){
        if(x%i==0)j=j*(i-1)/i;
        while(x%i==0)x/=i;
    }
    if(x!=1)j=j*(x-1)/x;
    return j;
}
ll che(ll x,ll y){
    ll z=(ll)((double)x*y/mo+1e-5)*mo;
    return x*y-z;
}
ll qsm(ll x,ll y){
    ll z=1;
    for(;y;y/=2,x=che(x,x))if(y&1)z=che(x,z);
    return z;
}
void fwt(ll *a,ll n){
    ll i,j,k,x,y;
    for(k=1;k<n;k*=2){
        for(i=0;i<n;i+=k*2){
            fo(j,0,k-1){
                x=a[i+j],y=a[i+j+k];
                a[i+j]=(x+y)%mo,a[i+j+k]=(x-y+mo)%mo;
            }
        }
    }
}
void ufwt(ll *a,ll n){
    ll i,j,k,x,y;
    for(k=1;k<n;k*=2){
        for(i=0;i<n;i+=k*2){
            fo(j,0,k-1){
                x=a[i+j],y=a[i+j+k];
                a[i+j]=(x+y)%mo,a[i+j+k]=(x-y+mo)%mo;
            }
        }
    }
}
void gao(ll *a,ll *b,ll n){
    ll i;
    fwt(a,n),fwt(b,n);
    fo(i,0,n)
    a[i]=che(a[i],qsm(b[i],ui+pp*(bz)));
    ufwt(a,n);
}
int main(){
//  freopen("fan.in","r",stdin);
//  freopen("fan.out","w",stdout);
    er[0]=1;fo(i,1,19)er[i]=er[i-1]*2;
    scanf("%lld%lld",&m,&mo);pp=phi(mo);
    ch=getchar();while(ch<'0'||ch>'9')ch=getchar();
    while(ch>='0'&&ch<='9'){
        ui=ui*10+ch-'0';if(ui>=pp)bz=1;ui%=pp;
        ch=getchar();
    }
    fo(i,0,er[m]-1)scanf("%lld",&a[i]);
    fo(i,0,m+1)scanf("%lld",&b[i]);
    fo(i,0,er[m]-1){
        fo(j,1,m)if(i&er[j-1])c[i]++;
        c[i]=b[c[i]];
    }
    mo*=er[m];
    gao(a,c,er[m]-1);
    fo(i,0,er[m]-1)printf("%lld\n",a[i]/er[m]);
}
發佈了433 篇原創文章 · 獲贊 631 · 訪問量 37萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章