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\n", ans1, ans2); } return 0; }
为了更好的明天