补题的时候去看了单调队列,贴一个最简单的最大子序和,有助于以后记不清楚的时候回顾一下。主要是注意head,tail的使用,具体表示什么,怎么判断±的条件。
这道题需要注意的是,教练每一次挪动区间以后,都会重置Maxrating = -1, cnt = 0,所以才会需要我们使用单调队列。因为就算你对每个区间统计,可是如果每移动一次不重置,仔细想想就跟整个数列的Max更换没有区别。
He starts to compare the rating of the contestants. He will pick a continous interval with length m, say [l,l+m−1], and then inspect each contestant from left to right. Initially, he will write down two numbers maxrating=−1 and count=0.
注意这段话……读的时候一直没读清楚啊orz
再一点是,区间长度规定为m,假如[a, b]区间的长度为m,那就是b - a + 1 = m。
这两点是我做的时候一直理解出错的地方……后来补题也是想不懂cnt怎么就18次了Orz,怎么区间就取了6个了。
关于单调队列我最开始理解的比较困难,现在是这么想的,单调队列一般是解决一个局部的连续的问题(相对于整体,不仅仅是区间连续,整个过程也是连续的),我们是通过控制循环里的i来控制区间的数目,Head tail来控制队列内容的改变,用数组来盛放队列。每一个i对应当前状态的一个队列。i更换,窗口向前滑动,就通过head,tail来改变队列的状态。
关于head tail的选取根据我们要递增还是递减,比如本题我们是逆着求严格递减,数据如下
3 2 2 1 5 7 6 8 2 9
9 2 8 6 7 5 1 2 2 3
第一个区间9 2 8 6 7 5,head指向9,tail指向5,因为是反着的对应回去原来的就相当于掉了个头。在判断前后关系的时候应该是head >= tail合法。
其实如果要我们求区间最大值并不难,我们维护一个严格递增序列,那么每个区间的Head就是最大值。但是这时候我们求cnt会非常麻烦。
这道题我觉得困难的地方就在这里:
- 逆着用单调队列
- 此时这个严格递减序列元素的数目就是当前的cnt
等等,你可能会问了,为什么正着求cnt就会麻烦?我们写一下正着求的时候这
3 2 2 1 5 7 6 8 2 9
1 5 7
1 5 6
1 5 6 8
1 5 6 8
5 6 8 9
也是每个序列元素的数目就是当前的cnt对不对!但是,如果我们这么求,我们就没法确定最大值了。因为维护严格递增的时候,我们要确保后面一个元素尽可能小,假如它小于队尾,那就pop_back,继续向前比较。为什么要这么做,是因为我们是求局部连续区间的某种递增递减子序列!注意!是子序列!subsequence!
然后就可以开始写代码了,还有两个小问题要注意
- 队列元素个数是tail - head,不是tail(因为我们在移动他们!)
- p,q必须使用ll
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
const int MAX_N = 1e7;
//ll n, m, k, p, q, r, MOD, T;
ll maxrating, cnt;
ll num[MAX_N + 10];
ll p, q;
int n, m, k, MOD, T, r;
int que[MAX_N + 10], head, tail;
int main()
{
scanf("%d", &T);
while (T--)
{
//printf("intoTloop\n");
scanf("%d%d%d%lld%lld%d%d", &n, &m, &k, &p, &q, &r, &MOD);
for(int i = 1;i <= k;i++)
{
scanf("%lld", &num[i]);
}
for(int i = k+1;i <= n;i++)
{
num[i] = (p * num[i-1] + q * i + r)%MOD;
}
//printf("into i loop\n");
head = tail = 0;
cnt = 0, maxrating = 0;
for(int i = n;i >= 1;i--)
{
//printf("%d\n", i);
while(head < tail && que[head] - i > m - 1)
head++;
while(tail > head && num[i] >= num[que[tail-1]])
tail--;
que[tail++] = i;
if(i <= n-m+1)
{
maxrating += num[que[head]] ^ i;
//cnt += tail;
cnt += (tail - head) ^ i;
}
}
printf("%lld %lld\n", maxrating, cnt);
}
return 0;
}