题意:n个人,对于区间长度m进行q次查询,问查询到最大值交换的次数
给定一个长度为n的序列,给出前k个值,后面根据公式自己算。现在问你每个区间长度为m的子序列最大值异或上i 和 最大值变化次数异或上i的求和。
思路:
倒着考虑, 就变成 一个下台阶的问题, 逆向思维..
队列 维护 位置, 寻找区间 就可以了.
用单调队列来维护,只要把序列倒过来维护递减序列。这样队列中元素个数就是count个数(倒的是递减,,, 正的就是递增,,, 所以每两个相邻都会让count加一,加上最开始0正好是队列个数),队头就是最大值。
注意:求a[i]和最终值可能过大
#include <iostream>
#include <algorithm>
#include <stdio.h>
#include <string.h>
#include <queue>
#include <string>
#include <map>
#include <math.h>
#include <set>
using namespace std;
#define LL long long
#define inf 0x3f3f3f3
//const int mod = 1e9+7;
const int N = 1e7+7;
int n,m,k,q,p,r,mod;
int a[N];
int qu[N]; ///单调栈
LL ans,sum;
void geti()
{
int head,rear;
head = rear =0;
for (int i = n; i >= 1; i--)
{
while (rear > head && a[qu[rear-1]] <= a[i]) ///存储单调递减数列
rear--;
qu[rear++] = i;
if (i > n-m+1) continue;
while (qu[head] > i+m-1) head++; ///移到区间内
//printf("%d %d %d %d\n",rear,head,rear-head,a[qu[head]]);
ans += (rear-head)^i;
sum += a[qu[head]]^i;
}
}
int main()
{
int t;
scanf("%d",&t);
while (t--)
{
sum = ans =0;
scanf("%d %d %d %d %d %d %d",&n,&m,&k,&p,&q,&r,&mod);
for (int i = 1; i <= k; i++)
scanf("%d",&a[i]);
for (int i = k+1; i <= n; i++)
a[i] = ((LL)p*a[i-1]%mod+(LL)q*i%mod+r%mod)%mod;
geti();
printf("%lld %lld\n",sum,ans);
}
return 0;
}