Ascending Rating(单调队列)

题目传送门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");
}

posted on 2019-01-25 21:57  PPXppx  阅读(135)  评论(0编辑  收藏  举报