题目
Ascending Rating
题意
给T个数组,每个数组n个元素,k个已知,未知的可用已知值求出,求每个长度为m的连续子区间,求区间中的最大值及最大值的变化次数
思路
第一反应是用单调队列来做,不过由于做题时没(tai)想(ruo)倒推的方法,才有下面的顺推。
在数组第ai位,(array[ai]表第ai位数组的值)
若队列为空,则以该位一个单调队列;
若队列不为空,
则更新单调队列。
如何较快的更新?
首先要将单调队列的第一位由array[ai-1]位变为array[ai],
若array[ai] < array[ai-1],则还要将后面比array[ai]大的未入队列的加入。这是队列前面的操作
此外,新加入的还有array[ai+m-1],如果它大于队列最大值,那么要加入队列。
草稿纸思路图示
由上可知,首先,我们需要一个可以从后读入,前端读出的容器,这个stl库中queue可以解决,但中间还需要插入,这就不好办了。
但是可以发现,每一个会进入队列的点,除了第一位,都是从左往右每个单调递减序列的起点。
为什么?因为当起点加入队列后,后面比它小都会被跳过,无法加入队列,直到起点弹出为止。而起点弹出只有两种情况:一种是队伍后有比该起点大的数,这时后面递减的只能排在第一位;而另一种,就是该单调队列比m要大,没有比起点大的数,但此时单调队列还是只有一个,因为后面的数还是会比在单调队列中(非起点)的小,直到下一个单调递减序列。(可能有点拗口。。)
从上,就可以有个想法,对一个连续子区间中,会加入队列的,除了现队列第一位,就只需考虑比它大的其中每个单调递减序列的起点。后方的修改只需要判断是否比队列最大值大即可,而前方,仔细想想,就会发现主要修改有两种:一是已入队的单调队列起点,队列-1即可,还有一种是未入队但比现队列第一位要大的,队列+1。对于可能时间复杂度,就略微优化一下(笑),可见代码。
#include<stdio.h>
#include<algorithm>
#include<string.h>
using namespace std;
//qunum用于储存队列长度,bnum用于记录队列最大值
int T,n,m,k,b,bnum,qunum;
long long a,resA,resB,p,q,r,MOD,plA,plB;
long long i[10000000+2],j[10000000+2];
/*
用过vector和queue,都没用,用标记数组初始化也会炸。
最后才想到用覆盖需要预处理的数组才过了, 因为这题前面数据用过
后,对后面数据再无影响。
*/
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d%d%d%lld%lld%lld%lld",&n,&m,&k,&p,&q,&r,&MOD);
resA = resB = 0;
// i数组用于储存数值
for(a = 1;a <= n;a ++)
{
if(a <= k)
{
scanf("%d",&i[a]);
}
else
{
i[a] = (p * i[a-1] + q * a + r) % MOD;
}
}
//j在这里是储存到往后单调减少的终点,用于优化
for(a = 1;a <= n;a ++)
{
b = a;
while(i[b+1] <= i[b] && b < n)
{
b++;
}
while(a < b)
{
j[a++] = b;
}
j[b] = b;
}
qunum = 0;
int bb;
// 主程序
// 这里如果第ai位进入过队列,则j[ai]变为-1
for(a = 1;a <= n-m+1;a ++)
{
//队列为空处理方式,这里本应qunum++,但数据处理时算第一位已出队,则无加
if(qunum == 0)
{
bnum = i[a];
}
//更新队列
b = j[a] + 1;
while(b <= a+m-1)
{
if(j[b] == -1 || j[a] == -1) break;
if(i[b] > i[a])
{
qunum ++;
i[a] = i[b];
bb = b;
b = j[b] + 1;
j[bb] = -1;
if(i[bb] > bnum)
{
bnum = i[bb];
}
}
else
{
b = j[b] + 1;
}
}
//末端处理
if(i[a+m-1] > bnum && j[a+m-1] != -1)
{
bnum = i[a+m-1];
j[a+m-1] = -1;
qunum ++;
}
//若该位入过队,此时减去该入队
if(j[a] == -1)
{
qunum --;
}
/*数据处理,由于我上面预先把第一位踢出了,所以下面
qunum要加上这位*/
plA = bnum;
plB = qunum + 1;
resA += (plA ^ a);
resB += (plB ^ a);
}
printf("%lld %lld\n",resA,resB);
}
return 0;
}
博主蒟蒻,难免有些说的不好或有误,欢迎大牛牪犇评论或私信联系博主交流。如有觉得讲解不理解处,也可私信博主交流一下。
未经博主允许,不得私自转载或用于其他商业用途,谢谢合作。