[bzoj3992][SDOI2015]序列统计

题目描述

小C有一个集合S,里面的元素都是小于M的非负整数。他用程序编写了一个数列生成器,可以生成一个长度为N的数列,数列中的每个数都属于集合S。
小C用这个生成器生成了许多这样的数列。但是小C有一个问题需要你的帮助:给定整数x,求所有可以生成出的,且满足数列中所有数的乘积mod M的值等于x的不同的数列的有多少个。小C认为,两个数列{Ai}和{Bi}不同,当且仅当至少存在一个整数i,满足Ai≠Bi。另外,小C认为这个问题的答案可能很大,因此他只需要你帮助他求出答案mod 1004535809的值就可以了。
1<=N<=109,3<=M<=8000,M为质数,1<=x<=M-1,输入数据保证集合S中元素不重复。

DP

我们设f[i,j]表示放到第i个数,此时乘积为j。
F[i,j]=F[i1,k]
满足k可以与集合内某数相乘模mo的结果为j。
N太大,但是我们显然可以用矩阵乘法优化。
我们把它化为另一种形式:
F[i+1,jk%mo]=F[i,j]C[k]
其中C[k]=0或1表示k这个数在集合里有没有出现。
这样做有什么用呢?

往FFT方面进军

M是质数,我们就可以找到它的原根r。
根据原根定义,我们可以把每一个m以内的数进行映射对应,我们设
ind[i]=j表示 rji(Mod m)
那对于任何多项式A我们令A[i]=A[ind[i]]
DP式变为
F[i+1,(j+k)%(m1)]=F[i,j]C[k]
“标准”卷积形式。
因此我们可以对多项式C进行快速幂,多项式乘法用FFT加速。
注意FFT最后大于m-2的项要加到其下标对于m-1取模的下标里,详见代码。

参考代码

#include<cstdio>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
typedef long long ll;
const ll maxm=8000+10,mo=1004535809,GG=3;
ll ind[maxm],a[maxm*3],b[maxm*3],c[maxm*3],e[maxm*3],f[maxm*3],s[maxm*3],w[maxm*3],tt[maxm*3];
double ce;
ll i,j,k,l,r,t,n,m,q,top,len,ni;
ll quicksortmi(ll x,ll y,ll z){
    if (!y) return 1;
    ll t=quicksortmi(x,y/2,z);
    t=t*t%z;
    if (y%2) t=t*(x%z)%z;
    return t;
}
void DFT(ll *a,ll sig){
    fo(i,0,len-1){
        ll p=0;
        for(ll j=0,tp=i;j<ce;j++,tp/=2) p=(p<<1)+(tp%2);
        tt[p]=a[i];
    }
    for(ll m=2;m<=len;m*=2){
        ll half=m/2,bei=len/m;
        fo(i,0,half-1){
            ll wi=(sig>0)?w[i*bei]:w[len-i*bei];
            for(ll j=i;j<len;j+=m){
                ll u=tt[j],v=tt[j+half]*wi%mo;
                tt[j]=(u+v)%mo;
                tt[j+half]=((u-v)%mo+mo)%mo;
            }
        }
    }
    if (sig==-1)
        fo(i,0,len-1) tt[i]=tt[i]*ni%mo;
    fo(i,0,len-1) a[i]=tt[i];
}
void FFT(ll *a,ll *b,ll *c){
    ll i;
    fo(i,0,len-1) e[i]=a[i],f[i]=b[i];
    DFT(e,1);DFT(f,1);
    fo(i,0,len-1) e[i]=e[i]*f[i]%mo,c[i]=0;
    DFT(e,-1);
    fo(i,0,len-1) c[i%(m-1)]=(c[i%(m-1)]+e[i])%mo;
    //fo(i,m-1,len-1) c[i]=0;
}
void solve(ll n){
    ll i;
    if (n==1){
        fo(i,0,len-1) b[i]=c[i];
        return;
    }
    solve(n/2);
    FFT(b,b,b);
    if (n%2) FFT(b,c,b);
}
int main(){
    scanf("%lld%lld%lld%lld",&n,&m,&q,&top);
    fo(i,1,top){
        scanf("%lld",&j);
        s[i]=j;
    }
    len=1;
    while (len<m*2) len*=2;
    ni=quicksortmi(len,mo-2,mo);
    ce=double(log(len)/log(2));
    w[0]=1;w[1]=quicksortmi(GG,(mo-1)/len,mo);
    fo(i,2,len) w[i]=w[i-1]*w[1]%mo;
    fo(i,1,m-1){
        fo(j,1,m-1){
            k=quicksortmi(i,j,m);
            if (k==1) break;
        }
        if (j==m-1){
            r=i;
            break;
        }
    }
    fo(j,0,m-2) ind[quicksortmi(i,j,m)]=j;
    a[ind[1]]=1;
    fo(i,1,top) 
        if (s[i]) c[ind[s[i]]]=1;
    solve(n);
    printf("%lld\n",b[ind[q]]);
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
BZOJ 2908 题目是一个数据下载任务。这个任务要求下载指定的数据文件,并统计文件中小于等于给定整数的数字个数。 为了完成这个任务,首先需要选择一个合适的网址来下载文件。我们可以使用一个网络爬虫库,如Python中的Requests库,来帮助我们完成文件下载的操作。 首先,我们需要使用Requests库中的get()方法来访问目标网址,并将目标文件下载到我们的本地计算机中。可以使用以下代码实现文件下载: ```python import requests url = '目标文件的网址' response = requests.get(url) with open('本地保存文件的路径', 'wb') as file: file.write(response.content) ``` 下载完成后,我们可以使用Python内置的open()函数打开已下载的文件,并按行读取文件内容。可以使用以下代码实现文件内容读取: ```python count = 0 with open('本地保存文件的路径', 'r') as file: for line in file: # 在这里实现对每一行数据的判断 # 如果小于等于给定整数,count 加 1 # 否则,不进行任何操作 ``` 在每一行的处理过程中,我们可以使用split()方法将一行数据分割成多个字符串,并使用int()函数将其转换为整数。然后,我们可以将该整数与给定整数进行比较,以判断是否小于等于给定整数。 最后,我们可以将统计结果打印出来,以满足题目的要求。 综上所述,以上是关于解决 BZOJ 2908 数据下载任务的简要步骤和代码实现。 希望对您有所帮助。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值