多校第三场第一题: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): 1339 Accepted Submission(s): 362

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=0 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,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 :
AB==∑i=1n−m+1(maxratingi⊕i)∑i=1n−m+1(counti⊕i)

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

这道题靠的是数据结构,没有接触过这样的类型题,所以思考很困难,有的时候正向思维比较难想,反过来想就好弄多了
题解:
按照 r 从 m 到 n 的顺序很难解决这个问题。
考虑按照 r 从 n 到 m 的顺序倒着求出每个区间的答案。
按照滑窗最大值的经典方法维护 a 的单调队列,那么队列 中的元素个数就是最大值的变化次数
O(n)的复杂度解决

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
const int inf = 0x3f3f3f3f;
const int maxn=1e7+5;
typedef struct Node{
    int value,pos;
    Node(){}
    Node(int v,int p){value = v;pos = p;}
}Node;
int a[maxn];
Node node[maxn];
LL A,B;
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n,m,k,p,q,r,mod;
        scanf("%d %d %d %d %d %d %d",&n,&m,&k,&p,&q,&r,&mod);
        A = 0;
        B = 0;
        for(int i = 1;i <= k;++i)
        {
            scanf("%d",&a[i]);
        }
        for(int i = k + 1;i <= n;++i)
        {
            a[i] = (1LL * p * a[i - 1] + 1LL * q * i + r) % mod;
        }
        int head = 1,tail = 0;
        for(int i = n;i >= 1;--i)
        {
            //删尾
            while(head <= tail && node[tail].value <= a[i]) tail--;
            //得到最优解并插入
            node[++tail] = Node(a[i],i);
            //去头
            while(node[head].pos - i >= m) head++;
            if(i <= n - m + 1){
                //printf("%d %d %d\n",node[head].value,tail - head + 1,index);
                A += (LL)(node[head].value ^ i);
                B += (LL)((tail - head + 1) ^ i);
            }
        }
        printf("%lld %lld\n",A,B);
    }
    return 0;
}







转载:https://blog.csdn.net/a_bright_ch/article/details/77076228
单调队列的介绍与使用:
单调队列顾名思义就是一个有规律的队列,这个队列的规律是:所有在队列里的数都必须按递增(或递减)的顺序列队,如果真有这么一个队列,那么队列的头是不是就是最小(或最大)的呢?

可以解决区间段内的最大值

例题:(来源:caioj 1172)

给定一个n个数的数列,从左至右输出每个长度为m的数列段内的最大数。

解法1:
如果按照常规方法,我们在求f[i]即i~i+m-1区间内的最值时,要把区间内的所有数都访问一遍,时间复杂度约为O(nm)。有没有一个快一点的算法呢?

解法2:
我们知道,上一种算法有一个地方是重复比较了,就是在找当前的f(i)的时候,i的前面k-1个数其它在算f(i-1)的时候我们就比较过了。那么我们能不能保存上一次的结果呢?当然主要是i的前k-1个数中的最大值了。答案是可以,这就要用到单调递减队列。

使用单调队列就涉及到去头和删尾:

1、队列的头一定是在一段时间前就加入了队列,现在的队列头会不会离开了我们处理的区间呢?如果它离我们正在处理的i太远了,我们就要把它去掉,去除冗杂的信息。

2、为了保证队列的递减性,在从列队尾新插入元素v时,要考虑队列尾的值是否大于v,如果是,队列呈现 队列尾-1的值 > 队列尾的值 > v ,此时队列递减性没有消失;如果不是,队列呈现 队列尾-1的值 > 队列尾的值 < v ,队列递减性被打破。

为了维护递减性,我们做如下考虑:v是最新值,它的位置是目前最靠后的,它可成为以后的最大值,必须留下;队列尾-1的值与v大小不定,不能冒然删去它;队列尾的值夹在v和队列尾-1之间,它不但不是最大值,对于以后的情况又不如v优,因为v相比队列尾更靠后(v可以影响到后m个值,队列尾只能影响到从它的位置往后数m-1个值),而且值更大,所以删队列尾是必定的。

在维护好一个 区间正确、严格递减 的单调递减队列后,队列头就是当前区间的最大值了。

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;

int a[200000];
struct node
{
    int x,p;
    node(){}
    node(int xx,int pp){x=xx;p=pp;}
}list[200000];

int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    int head=1,tail=1;
    list[1]=node(a[1],1);
    for(int i=2;i<=n;i++)
    {
        while(head<=tail&&list[tail].x<=a[i]) tail--;//删尾
        list[++tail]=node(a[i],i);//得到最优解并插入
        while(i-list[head].p>=m) head++;//去头
        if(i>=m) printf("%d\n",list[head]);
    }
    return 0;
}

整理归纳单调队列的定义:

1、维护区间最值;
2、去除冗杂状态;
3、保持队列单调(最大值是单调递减序列,最小值是单调递增序列);
4、最优选择在队首。

整理归纳单调队列的使用方法:

1、维护队首(对于上题就是如果你已经是当前的m个之前那你就可以被删了) ;
2、在队尾插入(每插入一个就要从队尾开始往前去除冗杂状态) ;
3、取出需要的最优解(队列头的值即是);
4、借助最优解,得到目前所求的最优解(通常此处插入DP方程)。

单调队列的原理:

在处理f[i]时,去除冗杂、多余的状态,使得每个状态在队列中只会出现一次;同时维护一个能瞬间得出最优解的队列,减少重新访问的时间;在取得自己所需的值后,为后续的求解做好准备。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值