HDU 6319 Problem A. Ascending Rating(单调队列详解)

题目链接

Problem A. Ascending Rating

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 524288/524288 K (Java/Others)
Total Submission(s): 2057    Accepted Submission(s): 622


 

Problem Description

Before the start of contest, there are n ICPC contestants waiting in a long queue. They are labeled by 1 to n from left to right. It can be easily found that the i-th contestant's QodeForces rating is ai.
Little Q, the coach of Quailty Normal University, is bored to just watch them waiting in the queue. 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. Everytime he meets a contestant k with strictly higher rating than maxrating, he will change maxrating to ak and count to count+1.
Little T is also a coach waiting for the contest. He knows Little Q is not good at counting, so he is wondering what are the correct final value of maxrating and count. Please write a program to figure out the answer.

 

Input

The first line of the input contains an integer T(1≤T≤2000), denoting the number of test cases.
In each test case, there are 7 integers n,m,k,p,q,r,MOD(1≤m,kn≤107,5≤p,q,r,MOD≤109) in the first line, denoting the number of contestants, the length of interval, and the parameters k,p,q,r,MOD.
In the next line, there are k integers a1,a2,...,ak(0≤ai≤109), denoting the rating of the first k contestants.
To reduce the large input, we will use the following generator. The numbers p,q,r and MOD are given initially. The values ai(k<in) are then produced as follows :

ai=(p×ai−1+q×i+r)modMOD


It is guaranteed that ∑n≤7×107 and ∑k≤2×106.

 

扫描二维码关注公众号,回复: 2704419 查看本文章

Output

Since the output file may be very large, let's denote maxratingi and counti as the result of interval [i,i+m−1].
For each test case, you need to print a single line containing two integers A and B, where :

AB==∑i=1nm+1(maxratingii)∑i=1nm+1(countii)


Note that ``⊕'' denotes binary XOR operation.

Sample Input

 

1

10 6

10 5 5 5 5

3 2 2 1 5 7 6 8 2 9

Sample Output

 

46 11

题意:

给你一个上面a[i]的公式,并且只给你k个数,让你按照公式求出剩下的n-k个数,得到一个a[1...n]的序列

然后对于该序列,每一个长度为m的连续区间,就一次该区间的最大值maxrate(初值为0)和最大值的变化次数count

然后再将这些值加起来A+=maxrate^i   B+=count^i

用2 3 1 2 5 6 2 8 9  m=4举例

对于第一个区间[1,4],maxrate=3,最大值变换次数为0->2->3 count=2

对于第二个[2,5] maxrate=5,count=2 0->3->5

对于第三个[3,6] maxrate=6,count=4,0->1->2->5->6

以此类推

解析:

单调队列:队列内的元素具有单调性(单调递增/递减),并且插入的时间也是具有单调性的
用单调递减队列(头节点为最大值,然后单调递减)来举例,维护一个长度为m的区间的最大值
1.删尾:插入一个元素,如果队尾元素>插入元素,那么就将插入元素插入到队尾就好了;
如果队尾元素<=插入元素,将队尾弹出,然后继续上述的操作,直至找到一个元素>插入元素。
2.去头:因为有些题目要求是固定长度的区间,即固定滑块,所以我们需要去头操作来把队列
里面下标不在当前区间的元素删掉。假定当前插入的元素是i,维护的是[i-m+1,i]的区间。
单调队列因为去尾的操作所以还具有一个性质,就是队列里里面元素的下标是从小到大,
即队列里面一个节点x,前面是值大于x的值,下标小于x的下标的节点,后面是值小于x的值,
下标大于x的下标的节点。那么根据这个性质,我们只要从队列头部开始把下标小于i-m+1的值都弹出就可以了。

所以单调队列其实是一个双端弹出、一段插入并且节点是同时按照值和插入时间的单调性排列的队列。

那么到本道题,这里有两个值,maxrate和count,maxrate就是区间的最大值,count是最大值的变化次数
从这里来说,如果我们正序扫的话,一个区间内,在最大值之前比最大值小的元素x
(并且假定这个元素会对count进行一次答案的贡献+1)可能会被在最大值之后,比x的值大的元素所替代。
那么这个就不对了,我们希望队列里维护的就是区间内count有贡献的值。分析原因可以知道,在区间最大值之后的元素
最大值应该是他们屏蔽的,而正序扫无法达到这种效果。
那么我们就可以逆着扫,队列内也是维护最大值,那么此时队列内值从大到小,下标也是从大到小。并且我们可以发现
在一个区间,逆着扫,一旦最大值插入后,就会把队列内的元素(这些元素就是上面说的在最大值之后,
应该被最大值屏蔽的元素)全部弹出。这样,每一个小范围内的最大值,都会屏蔽之后比他小的元素,直至有一个比他大的元素
出现。然后我们再维护区间长度的约束就可以了。

这里全部用int,就A,B用ll会快一点,差不多省了1s的时间

不过比赛的时候肯定也不会冒险去用int的,long long 保险一点也可以了,

#include <cstdio>
#include <cstring>
#include <vector>
#include <set>
#include <queue>
#include <algorithm>
#define lch root<<1
#define rch (root<<1)|1
using namespace std;
typedef long long ll;

const int MAXN = 1e7+10;



typedef struct node
{
	int id;
	int x;
	//node(int a=0,ll b=-1):id(a),x(b){}
}node;

node a[MAXN];


int n;
node mq[MAXN];






int main()
{
	int t;
	scanf("%d",&t);
	int m,k,p,q,r,mod;
	while(t--)
	{
		scanf("%d%d%d%d%d%d%d",&n,&m,&k,&p,&q,&r,&mod);
		for(int i=1;i<=k;i++)
			scanf("%lld",&a[i].x),a[i].id=i;
		for(int i=k+1;i<=n;i++)
			a[i].x=(1ll*p*a[i-1].x+1ll*q*i+1ll*r)%mod,a[i].id=i;


		ll A,B;
		A=B=0;
		int head,tail;
		head=tail=0;
		for(int i=n;i>=1;i--)
		{
			while(head<tail&&mq[tail-1].x<=a[i].x)
				tail--;
			mq[tail++]=a[i];
			while(head<tail&&mq[head].id>i+m-1)
				head++;
			if(i<=n-m+1)
			{
				A+=(ll)((mq[head].x)^i);
				B+=(ll)((tail-head)^i);
			}
		}
		printf("%lld %lld\n",A,B);
	}
	return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_37025443/article/details/81297406