考虑分块
把n个序列分成长度为k的若干段,当然最后一段不一定是k。
然后对每一块求一个前缀、后缀乘积和。
最后暴枚区间,注意到每个区间刚好会被两段或一段覆盖,直接O(1)算即可。
#include<cstdio>
using namespace std;
typedef long long ll;
const int N=2e7+1;
int n,k,mod,b,c,d,ans;
int s[N],pre[N],suf[N];
int main()
{
freopen("range.in","r",stdin); freopen("range.out","w",stdout);
scanf("%d%d%d",&n,&k,&mod);
scanf("%d%d%d%d",&s[1],&b,&c,&d);
for(int i=2; i<=n; i++) s[i]=(s[i-1]*1ll*b+c)%d;
int num=n/k;
for(int i=0; i<num; i++)
{
int t=i*k+1;
pre[t]=s[t];
for(int j=t+1; j<t+k; j++) pre[j]=pre[j-1]*1ll*s[j]%mod;
suf[t+k-1]=s[t+k-1];
for(int j=t+k-2; j>=t; j--) suf[j]=suf[j+1]*1ll*s[j]%mod;
}
if(n%k)
{
int t=num*k+1;
pre[t]=s[t];
for(int i=t+1; i<=n; i++) pre[i]=pre[i-1]*1ll*s[i]%mod;
suf[n]=s[n];
for(int i=n-1; i>=t; i--) suf[i]=suf[i+1]*1ll*s[i]%mod;
}
for(int i=1; i<=n-k+1; i++)
if(i%k==1) ans^=pre[i+k-1]; else
ans^=suf[i]*1ll*pre[i+k-1]%mod;
printf("%d",ans);
return 0;
}