HDU 6319 Ascending Rating (单调双端队列)

题意:给定一个序列a[1..n],对于每个长度为m的连续子区间,求出区间的最大值和从左往右扫描该区间最大值的变化次数。

分析:先O(n)处理出整个序列的值。求出每个长度为m的连续区间中的最大值可以用单调队列求出,但同时要维护区间最大值的变化次数,即以区间最左边的元素为最小值的递增序列的长度。如果单纯地从左往右更新单调队列的话,每次窗口的最左端出队列后,队列中的单调特性就又被打破,而这时要重新确定这个递增序列,就必须重新遍历一遍队列中的元素,肯定超时。

换个思路,如果我们从右往左滑动这个窗口,那么每次将左边窗口外的元素ai加入队列后,都能使其中元素的个数为该区间的以ai为最小值的递增序列的个数。队列中单调递减,队首为最大值,队尾为当前加入的元素。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<LL, LL> PLL;
const int MAXN = 1e7+10;
int N, M;
LL arr[MAXN];
int k, p, q, r, mod;
int deq[MAXN],deq2[MAXN];

void generate()
{
    for( int i = k+1; i <= N; i++ )
        arr[i] = ((LL)p *arr[i-1]%mod + (LL)q *i%mod + r%mod) %mod;
}
LL ans1,ans2;
void solve()
{
    ans1=ans2=0;
    int s=0,t=0;
    int x = N-M+1;
    for(int i=N;i>=N-M+1;--i){
        while(s<t&& arr[deq[t-1]]<=arr[i]) --t;
        deq[t++]=i;
    }
    ans1 = x^arr[deq[s]];
    ans2 = x^(t-s);
    x--;
    for(int i=N-M;i>=1;--i){
        if(deq[s]==i+M)       //窗口的尾部是队首,则出队
            s++;
        while(s<t&& arr[deq[t-1]]<=arr[i]) --t;
        deq[t++]=i;
        ans1 += (arr[deq[s]])^x;
        ans2 += (t-s)^x;
        x--;
    }
}

int main()
{
    #ifndef ONLINE_JUDGE
         freopen("in.txt","r",stdin);
         freopen("out.txt","w",stdout);
    #endif
    int T;  cin >> T;
    while(T--){
        scanf("%d%d", &N, &M);
        scanf("%d%d%d%d%d", &k,&p, &q, &r, &mod);
        for( int i = 1;i <= k; i++)
            scanf("%lld", arr+i);
        generate();
        solve();
        printf("%lld %lld
", ans1, ans2);
    }
    return 0;
}
为了更好的明天
原文地址:https://www.cnblogs.com/xiuwenli/p/9392603.html