Ascending Rating(单调队列)
给定一个序列\(a[1...n]\),对于每个长度为\(m\)的连续子区间,求出区间的最大值以及从左往右扫描该区间时最大值的变化次数。
考虑到输出可能很大,分别求出异或和即可.
一个长度为n的序列,给出序列的前k个值,剩下的\([k+1,n]\)可以由题目给出的公式推出.
把每个长度为m的区间看作滑动窗口,其实本题就是滑动窗口的升级版.
while(T--){
ans1=ans2=0;
n=read();m=read();k=read();
p=read();q=read();r=read();mod=read();
//p,q,r,mod仅用于推出剩下的序列
for(int i=1;i<=k;i++)a[i]=read();
//给出序列的前k个值
for(int i=k+1;i<=n;i++)
a[i]=(1LL*p*a[i-1]+1LL*q*i+r)%mod;
//由题目给出的公式推出剩下的序列
int l=1,r=0;
for(int i=n;i>=1;i--){//从后往前扫描
while(l<=r&&a[Q[r]]<=a[i])r--;
//队列Q中记录的是元素的编号(1~n),而不是元素的值
//构建单调递减的队列
Q[++r]=i;
//将这个元素入队
if(i+m-1<=n){//小区间m全部在序列n中
while(Q[l]>=i+m)l++;
//把"过期"的元素踢出队列
ans1+=i^a[Q[l]];
ans2+=i^(r-l+1);
//因为我们构建的是单调递减队列,所以队头元素是最大值.
//又因为是从后往前扫描,相当于从前往后的单调递增队列
//所以队列长度就是最大值更新的次数
}
}
printf("%lld %lld",ans1,ans2);
printf("\n");
}