【JZOJ4425】【HNOI2016模拟4.4】Fenwit

来源:互联网 发布:测试键盘按键软件 编辑:程序博客网 时间:2024/06/05 07:00

Description

这里写图片描述

Data Constraint

这里写图片描述

Solution

这道题很多人用FWT过了,毕竟题目名就这样……
那我们另辟蹊径,想想矩阵乘法怎么做。
我们要确定F0的任何一项j对于Ft的任何一项k的系数。那么我们发现j转移至k的途中系数只与转移a^b的1的个数有关。所以我们发现这T次转移只是个填1的问题:现在有一个T行m列的矩阵,每一行你要填上若干个1,是的最后j转移至k。那我们发现对于一个填1的问题,我们不关心她到底是什么数,因为在原本需要填1填1会使下次需要填1的地方-1,在原本需要填0填1会使下次需要填1的地方+1。于是我们设出f[i][j]表示当前做到第i步,有j个地方与要填1的系数,做一下dp。对于一个j,k而言,若j与k有x个地方需要修改,那么初始值f[0][x]=1。我们发现相邻两步的转移是相同的,所以就够除了一个矩阵,做一遍矩阵乘法就好。那么我们发现对于一个确定的k而言,只要j与k有相同的x,j是多少并不影响转移,因为初始时f[0][x]同样是1。所以我们设出s[j][p]表示与j有p为不同的数的f0的和,最后计算一下就好。

Code

#include<iostream>#include<cmath>#include<cstring>#include<cstdio>#include<algorithm>#define ll long longusing namespace std;const int maxn=(1<<20);ll a[maxn],b[maxn],c1[20][20],d[maxn],ans1[maxn],g[maxn][20];ll n,m,p,i,t,j,k,l,x,y,z,len;struct code{    ll a[19][19];    code friend operator * (code x,code y){        code z;memset(z.a,0,sizeof(z.a));int i,j,k;        for (i=0;i<=18;i++)            for (j=0;j<=18;j++){                for(k=0;k<=18;k++)                     z.a[i][j]=(z.a[i][j]+x.a[i][k]*y.a[k][j]%p);                z.a[i][j]%=p;            }        return z;    }}c,a1,b1;char s[maxn];void make(){    ll t=0,i,j,k=0;    for (i=1;i<=len;i++){        k=k*10+(s[i]-48);        s[i]=k/2+48;k%=2;    }    if (s[1]==48){        for (i=2;i<=len;i++)            swap(s[i],s[i-1]);        len--;      }}void dg(){    while (len>1 || s[1]>49){        d[0]++;if (s[len]%2) d[d[0]]=1;        make();    }    a1=c;    for (;d[0]>0;d[0]--){        a1=a1*a1;        if (d[d[0]]) a1=a1*c;    }}int main(){//  freopen("data.in","r",stdin);freopen("data.out","w",stdout);    scanf("%lld%lld%s\n",&m,&p,s+1);n=(1<<m);len=strlen(s+1);    c1[0][0]=1;    for (i=1;i<=m;i++){        c1[i][0]=1;        for (j=1;j<=i;j++)            c1[i][j]=(c1[i-1][j-1]+c1[i-1][j])%p;    }    for (i=0;i<n;i++) scanf("%lld",&a[i]);    for (i=0;i<=m;i++) scanf("%lld",&b[i]);    for (i=0;i<=m;i++)        for (k=0;k<=i;k++)            for (l=0;l<=m-i;l++)                c.a[i][i-k+l]=(c.a[i][i-k+l]+b[k+l]*c1[i][k]%p*c1[m-i][l]%p)%p;    dg();    for (i=0;i<=m;i++){        memset(b1.a[0],0,sizeof(b1.a[0]));        b1.a[0][i]=1;b1=b1*a1;        ans1[i]=b1.a[0][0];    }    for (i=0;i<n;i++)        g[i][0]=a[i];    for (i=1;i<=m;i++)        for (j=i;j>0;j--)            for (k=0;k<n;k++){                if (k&(1<<(i-1))) t=k-(1<<(i-1));                else t=k+(1<<(i-1));                g[t][j]=(g[t][j]+g[k][j-1])%p;            }    for (i=0;i<n;i++){        x=0;        for (j=0;j<=m;j++)            x=x+g[i][j]*ans1[j]%p;        x%=p;        printf("%lld\n",x);    }}
原创粉丝点击