[分块]JZOJ 100035 区间

Description

 

Input

Output

 

Sample Input

sample1:
4 2 10
5 1 1 10
sample2:
1000 97 96998351
41 1668 505 2333

Sample Output

sample1:
4
sample2:
1749769
 

Data Constraint

分析

这题赛场打的是部分分,逆元前缀乘积40points

然后比赛结束看题解发现自己最早的想法已经接近正解了(fk u)

一开始是想分块,维护区间乘积,然后空间和时间都不接受

其实只需要维护块内前缀乘积和后缀乘积即可,这样可以O(1)得到乘积并异或上去

#include <iostream>
#include <cstdio>
using namespace std;
const int N=10000001;
int s[2*N];
int sumq[2*N],sumn[2*N];
int n,k,m,P,a,b,c,d;

void Block(int x) {
    int l=(x-1)*k+1,r=min(x*k,n);
    sumq[l]=s[l];sumn[r]=s[r];
    for (int i=l+1;i<=r;i++)
    sumq[i]=1ll*sumq[i-1]*s[i]%P;
    for (int i=r-1;i>=l;i--)
    sumn[i]=1ll*sumn[i+1]*s[i]%P;
}

int main() {
    freopen("range.in","r",stdin);
    freopen("range.out","w",stdout);
    scanf("%d%d%d",&n,&k,&P);
    scanf("%d%d%d%d",&a,&b,&c,&d);
    s[1]=a;
    for (int i=2;i<=n;i++) s[i]=(1ll*s[i-1]*b+c)%d;
    int m=n/k;
    if (m*k<n) m++;
    for (int i=1;i<=m;i++) Block(i);
    long long ans=0;
    for (int i=1;i<=n-k+1;i++) {
        int sum=1;
        if (!((i-1)%k)) sum=sumq[min(i+k-1,n)];
        else sum=1ll*sumn[i]*sumq[min(i+k-1,n)]%P;
        ans^=1ll*sum;
    }
    printf("%lld",ans);
    fclose(stdin);fclose(stdout);
}
View Code

 

posted @ 2018-08-09 20:06  Vagari  阅读(140)  评论(0编辑  收藏  举报