hdu6319 Problem A. Ascending Rating

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

按照 r 从 m 到 n 的顺序很难解决这个问题。
考虑按照 r 从 n 到 m 的顺序倒着求出每个区间的答案。
按照滑窗最大值的经典方法维护 a 的单调队列,那么队列
中的元素个数就是最大值的变化次数。
时间复杂度 O(n)。

#include"bits/stdc++.h"
using namespace std;
typedef long long ll;
ll n,m,k,p,q,r,mod;
ll a[10000007];
ll qp[20000007];
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(int i=1;i<=k;i++){
            scanf("%lld",&a[i]);
        }
        for(int i=k+1;i<=n;i++){
            a[i]=(a[i-1]*p+q*i+r)%mod;
        }
        int top=0,bot=1;
        ll sa=0,sb=0;
        for(ll i=n;i>=1;i--){
            while(top>=bot&&a[i]>=a[qp[top]]) top--;
            qp[++top]=i;
            int ed=i+m-1;
            if(i<=n-m+1){
                while(qp[bot]>ed) bot++;
                sa+=(a[qp[bot]]^i);
                sb+=(i^(top-bot+1));
            }
        }
        printf("%lld %lld\n",sa,sb);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值