1. 程式人生 > >多校第三場第一題:Problem A. Ascending Rating

多校第三場第一題: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]時,去除冗雜、多餘的狀態,使得每個狀態在佇列中只會出現一次;同時維護一個能瞬間得出最優解的佇列,減少重新訪問的時間;在取得自己所需的值後,為後續的求解做好準備。