hdu 6319蒟蒻顺推写法

题目
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],如果它大于队列最大值,那么要加入队列。

草稿纸思路图示
第一步

第一个3出队,6<7不加,更新23入队

 2出队8>7入队
3出队
1出队,9入队

由上可知,首先,我们需要一个可以从后读入,前端读出的容器,这个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;
}

博主蒟蒻,难免有些说的不好或有误,欢迎大牛牪犇评论或私信联系博主交流。如有觉得讲解不理解处,也可私信博主交流一下。
未经博主允许,不得私自转载或用于其他商业用途,谢谢合作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值