题意:

有一个长度为m的窗口,一个长度为n的序列,现在将这个窗口在序列上滑动,问每次滑动这个窗口内数的乘积模p的总和为多少。

思路:

可以将序列分成长度为m的若干块,每次滑动窗口的乘积可以用前一块的后缀积与后一块的前缀积拼起来得到。所以预处理出每个数在自己的块内的前缀积和后缀积即可。

挺巧妙的。

代码:

typedef long long ll;
ll a[1000010];
ll pre[1000010];
ll suf[1000010];
int main()
{
    int n,m;
    ll p,x,y,z;
    while(scanf("%d%d%lld",&n,&m,&p)!=EOF)
    {
        scanf("%lld%lld%lld%lld",&a[0],&x,&y,&z);
        for(int i=1;i<n;i++)
            a[i]=((((x*a[i-1])%p*a[i-1])%p+(y*a[i-1])%p)%p+z)%p;
        /*for(int i=0;i<n;i++)
            scanf("%lld",&a[i]);*/
        /*for(int i=0;i<n;i++)
            printf("%lld ",a[i]);
        printf("\n");*/
        for(int i=0;i<n;i++)
        {
            if(i%m==0)pre[i]=a[i]%p;
            else pre[i]=(pre[i-1]*a[i])%p;
        }
        for(int i=n-1;i>=0;i--)
        {
            if(i%m==m-1||i==n-1)suf[i]=a[i]%p;
            else suf[i]=(suf[i+1]*a[i])%p;
        }
        /*for(int i=0;i<n;i++)
            printf("%lld ",pre[i]);
        printf("\n");
        for(int i=0;i<n;i++)
            printf("%lld ",suf[i]);
        printf("\n");*/
        ll ans=0;
        for(int i=0;i<=n-m;i++)
        {
            ll temp=1;
            temp=(temp*suf[i])%p;
            if(i%m!=0)temp=(temp*pre[i+m-1])%p;
            //printf("%lld ",temp);
            ans+=temp;
        }
        //printf("\n");
        printf("%lld\n",ans);
    }
    return 0;
}

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!

2017UrumqiA 贪心+概率DP Previous
徐州邀请赛F 树状数组+dfs序思想 Next