HDU 6319 Problem A. Ascending Rating 【单调队列】(2018 MUTC 3)

Problem A. Ascending Rating

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


 

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=−1and 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,k≤n≤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<i≤n) are then produced as follows :

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


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

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 :


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

题目大意:

输入一个t表示有几组数据,第二行输入七个数字,其中k代表接下来的一行要输入k个选手的信息,但是一共有n个选手,如果k<n,那么剩下的选手信息就由题目所给的公式补齐。然后给出了要询问的区间大小m,也就是说要求出[1,m],[2,m+1]……[n-m+1,n]每一个区间的最大值,和区间的最大上升的序列长度都异或“区间的left点”分别的总和,并输出。

解法:

由题目大意可以知道,left的取值范围是[1,n-m+1],知道范围后,我们就好操作了,不要想着嵌套两层for暴力去跑答案,虽然给了你5s的时间,但是他的n的范围给的也是很大,足足有10^7。足够让你TLE了。

先处理求最大值的,方便理解也比较简单,我们先处理第一组数据。也就是范围为[1,m]区间的数据。我们用数组模拟一个单调的队列,大的值放在前面,假设现在要放进数组里的值是a,比在数组最后一个值b要大的时候,我们就删除最后一个数b,继续往前比较,也就是说比a小的数,全部都拿出来,确保a前面都是比它大的数。而且放进数组里面的值并不是a值本身,而是a值得下标。

//a数组是放n个选手信息的数组 
while(a[j] >= a[maxrating[tep-1]]){//tep是最大值下标的数组大小 
	tep--;//遇到前面的值比当前值要小或者等于,就大小-1,继续往前比较 
	if(tep == 0)//数组下标不可能低于0,等于0的时候,代表没有数了在里面了 
		break;
}
maxrating[tep++] = j; //往里面放值的下标了

然后初始化一个的变量值为0(假设为left),我们每次去找最大值的时候,只需要判断这个模拟队列的数组里的下标是不是在这个区间的范围内就可以了,找到了,更新left的值为当前队列数组的第几个,就可以直接跳出循环。然后下次找的时候就从队列数组下标为left的地方开始找,因为随着区间的移动,前面一定是不符合的,所以我们没有必要浪费时间去跑一遍。

for(ll i = left;i < tep; i++){
	if(j - m + 1 <= maxrating[i] && maxrating[i] <= j){
		summaxrating += a[maxrating[i]] ^ ( j - m + 1 );
		left = i;
		break;
	}
}

就拿样例来解释一下吧,区间范围是6,所以我们第一次循环处理第一组数据处理的是 3,2,2,1,5,7。(以下讲的都是把数字的下标放进去,为了方便理解,直接讲放值)

 因为3是第一个数,直接放进队列数组里,然后2比3小,继续放入2=2,就跳过,不用理它,因为已经有了最大值,相等也不是上升序列,所以不需要相同的值。然后1也放进数组里,这时候数组里有3,2,1。一共3个数组,然后到了5,5比数组里所有的数字都大,全部拿开,数组里只放一个5,然后要放7,7比5大。5拿开,放7。然后判断最大值就是7

第二组数据[2,m+1],队列数组则多了一个6,但是7的下标还是在区间里,最大值还是7。到了第三组数据[3,m+2],队列数组里有7和6,然后8比7和6都打,全部拿出来,只放一个8,最大值就是8……以此类推。每次遇到的第一个下标范围在区间内的,就是最大值,因为大的值永远在前面。

要求count的个数的时候,则是反过来求,先从第n个数开始往后倒,原理是一样的,放进数组最大值,因为逆过来了,就是一个上升的区间,判断有多少个在区间范围内的,然后定义一个变量记录当前位置,下次直接从这个位置开始找,节省很多时间。附上AC代码(大概3s能过题):

#include <bits/stdc++.h>
#define ll long long 
using namespace std;
const ll MAXN = 10000005;
ll a[MAXN],counting[MAXN],maxrating[MAXN];
//a是大小为K的数组,counting数组是计算当前区间的最大上升序列数字的下标 
ll n,m,k,p,q,r,mod;
int main(){
	int t;
	scanf("%d", &t);
	while(t--){
		scanf("%lld%lld%lld%lld%lld%lld%lld", &n, &m, &k, &p, &q, &r, &mod);
		for(ll i = 1;i <= k; i++)
			scanf("%lld", &a[i]);
		while(k < n){
			a[++k] = ( p * a[k-1] + q * k + r ) % mod;//补齐 
		}
		ll summaxrating = 0,allcount = 0,tep = 0;//tep初始化为0,设置为maxrating数组的大小 
		for(ll j = 1; j <= m; j++){//先处理1-m这个区间的 
			if(j == 1){
				maxrating[tep++] = j;
			}else{
				while(a[j] >= a[maxrating[tep-1]]){
					tep--;
					if(tep == 0) break;	
				}
				maxrating[tep++] = j;
			}
		}
		ll left = 0;
		summaxrating += a[maxrating[0]]^1;
		for(ll j = m + 1; j <= n; j++){
			while(a[j] >= a[maxrating[tep-1]]){
				tep--;
				if(tep <= left && tep != 0) left--;
				if(tep == 0) break;
			}
			maxrating[tep++] = j;
			for(ll i = left;i < tep; i++){
				if(j - m + 1 <= maxrating[i] && maxrating[i] <= j){
					summaxrating += a[maxrating[i]] ^ ( j - m + 1 );
					left = i;
					break;
				}
			}
		}	
		tep = 0;
		for(ll i = n;i >= n - m + 1; i--){//从前往后处理 
			if(i == n){
				counting[tep++] = n;
			}else{
				while(a[i] >= a[counting[tep-1]]){
					tep--;
					if(tep == 0) break;
				}
				counting[tep++] = i;
			}
		}
		allcount = tep ^ ( n - m + 1 );
		left = 0;
		for(ll i = n - m ; i >= 1; i--){
			while(a[i] >= a[counting[tep-1]]){
				tep--;
				if(tep <= left && tep != 0) left--;
				if(tep == 0) break;
			}
			counting[tep++] = i;
			for(ll j = left;j < tep; j++){
				if(counting[j] <= i + m - 1){
					allcount += (tep - j) ^ i;
					left = j;				
					break;
				}
			}
		}
		printf("%lld %lld\n",summaxrating,allcount);
	}
}
发布了22 篇原创文章 · 获赞 19 · 访问量 3548

猜你喜欢

转载自blog.csdn.net/KnightHONG/article/details/81303679