hdu——6319- Ascending Rating

6319- Ascending Rating

题意:有一个长度为n的数字序列,只给出前k项,后n-k项由给出的公式从前一项计算出,求每个长度为m的区间中的最大值和以区间第一个数开头的上升序列的长度与区间第一个数的位置下标的异或值的和分别是多少?通俗的讲,主要就是求区间最大值和固定起点的区间上升序列的长度。

题解:本题考虑从后面往前面用滑动窗口加双向队列求解。从最后一个开始,如果当前的数比它后面一个数小,那么这就能组成一个递增序列就将它入队,但是当它比后面的数大的时候,想一下本题的要求,是固定了前面的来选后面的,如果前面有更大的,最大值已经被更新了,遇到小的就不会更新了,所以这时候,我们就要把队列里已经入队的比它小的数出队,然后再把这个数入队。这样子到最后我们队列的队首一定是区间最大值,因为对于队列中每一个数来说,在它之前比它小的数都被出队了,而到某个位置时,队列的大小就是这个区间的上升序列,此外我们还要处理一个问题,那就是已经不在当前区间中的最大值,我们应该从队列中删除,这样才能更新区间最大值。怎么判断一个值在不在区间中?我们可以通过存每个数的下标来做到。答案就在每个长度为m的区间中不断更新就是了。注意这里如果直接用deque双向队列会被卡常数t掉,所以需要手写双向队列,原理都一样。

附上标程:

#include
using namespace std;
const int maxn=10000010;
int a[maxn],d[maxn];
int t,n,m,k,p,q,r,mod;
long long A,B;
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d%d%d%d%d%d",&n,&m,&k,&p,&q,&r,&mod);
        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;
        for(int h=1,t=A=B=0,i=n; i; i--)
        {
            while(h<=t&&a[d[t]]<=a[i])
                  t--;
            d[++t]=i;
            if(i+m-1<=n)
            {
                while(d[h]>=i+m)h++;
                A+=i^a[d[h]];
                B+=i^(t-h+1);
            }
        }
        printf("%lld %lld\n",A,B);
    }
}

你可能感兴趣的:(多校,思维)