The 2018 ACM-ICPC China JiangSu Provincial Programming Contest G. Window (level 2 分块思维)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_37025443/article/details/86523527

题目链接

题意:

给你一个长度为n的数组A(由题目给的产生式得出),然后有一个长度为m的窗口在数组A上,起始下标在[1,m]位置上
他会不断向右滑动,每一次向右移动一个单位长度。你需要计算的是每一次窗口覆盖的区间内所有数的乘积%P

最后输出的是这些乘积之和(注意这里不需要%P)

解析:

一开始用扩展欧几里得定理做,因为对于当前窗口[i,j]的答案是res,那么下一次窗口的答案res/A[i]*A[j]%P

但是这里面A[i]和P不一定互质,所以无法用到乘法逆元,网上搜了怎么求(a/c) mod p(c,p不互质)

看到有两种

1.扩展欧几里得

2.公式

扫描二维码关注公众号,回复: 5018246 查看本文章

用第一种方法T了,用第二种方法答案不对。

后来想了想,这里面A[i]都是mod p后的结果,所以代码里求的是 (a mod p) / (c mod p) mod p

但是我实际上要求的是 (a/c) mod p

用第一种方法,好像可以行.....但是T了....好像是题目n太大的原因(但是我想想好像原理又不太对.....)

第二种方法,它是换模数求的,所以最后求出来的是(a mod p) / (c mod p) mod p的结果,而不是 (a/c) mod p

即题目所求的是A[i+1...j] mod p,但是求出来的是(A[i...j] mod p ) / A[i]  mod p的答案

然后以为是数学公式搜了题解...但是是一道思维题........

题解就是将n分块,分成\left \lceil \frac{n}{m}\right \rceil

然后对每一块独立的求前缀积和后缀积存在下标对应的数组种

然后当窗口刚好是完整的一块是,我们就用结尾元素的前缀积来做

如果不是,那么就肯定是起点在前一块,结尾在后一块,那么答案就是起始点的后缀积*结尾的前缀积%P

因为这道题对于一个滑动窗口,满足连续性ANS(A[i…j])=ANS(A[i….k])*ANS(A[k+1….j])  i<=k<j

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;

typedef long long int ll;
const int MAXN = 1e6+100;
int A[MAXN],P;
int pre[MAXN],bac[MAXN];


int main()
{
	
	int n,m;
	while(~scanf("%d%d%d",&n,&m,&P))
	{
		int x,y,z;
		scanf("%d%d%d%d",&A[1],&x,&y,&z);
		ll ans=1;
		A[1]%=P;
		int res=A[1];
        ans=res;
		for(int i=0;i<n/m+(n%m?1:0);i++)
        {
            for(int j=i*m+1;j<=i*m+m&&j<=n;j++)
            {
                if(j==1) continue;
                A[j]=((1ll*x*res%P*res%P+1ll*y*res%P)%P+z%P)%P;
                res=A[j];
                if(!i) ans=ans*A[j]%P;
            }
            int tmp=1;
            for(int j=i*m+1;j<=i*m+m&&j<=n;j++)
            {
                tmp=1ll*tmp*A[j]%P;
                pre[j]=tmp;
            }
        	tmp=1;
            for(int j=min(i*m+m,n);j>=i*m+1;j--)
            {
                tmp=1ll*A[j]*tmp%P;
                bac[j]=tmp;
            }
        }
        for(int i=m+1;i<=n;i++)
        {
           
            if(i%m==0)
                ans=ans+pre[i];
            else
            {
                ans=ans+1ll*bac[i-m+1]*pre[i]%P;    
            }
        }
        printf("%lld\n",ans);		
	}
	return 0;

}

猜你喜欢

转载自blog.csdn.net/qq_37025443/article/details/86523527