hdu——6319- Ascending Rating
6319- Ascending Rating
題意:有一個長度為n的數字序列,只給出前k項,後n-k項由給出的公式從前一項計算出,求每個長度為m的區間中的最大值和以區間第一個數開頭的上升序列的長度與區間第一個數的位置下標的異或值的和分別是多少?通俗的講,主要就是求區間最大值和固定起點的區間上升序列的長度。
題解:本題考慮從後面往前面用滑動視窗加雙向佇列求解。從最後一個開始,如果當前的數比它後面一個數小,那麼這就能組成一個遞增序列就將它入隊,但是當它比後面的數大的時候,想一下本題的要求,是固定了前面的來選後面的,如果前面有更大的,最大值已經被更新了,遇到小的就不會更新了,所以這時候,我們就要把佇列裡已經入隊的比它小的數出隊,然後再把這個數入隊。這樣子到最後我們佇列的隊首一定是區間最大值,因為對於佇列中每一個數來說,在它之前比它小的數都被出隊了,而到某個位置時,佇列的大小就是這個區間的上升序列,此外我們還要處理一個問題,那就是已經不在當前區間中的最大值,我們應該從佇列中刪除,這樣才能更新區間最大值。怎麼判斷一個值在不在區間中?我們可以通過存每個數的下標來做到。答案就在每個長度為m的區間中不斷更新就是了。注意這裡如果直接用deque雙向佇列會被卡常數t掉,所以需要手寫雙向佇列,原理都一樣。
附上標程:
#include<bits/stdc++.h>
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);
}
}