简述题意:
给定一个排列,多次询问,求一个区间 [ l , r ] [l,r] [l,r] 有多少个子区间的值都在区间 [ x , y ] [x,y] [x,y] 内。
算法1:
对于每个询问暴力枚举子区间,然后暴力检测是否满足条件,时间复杂度 O ( q n 3 ) O(qn^3) O(qn3)。
用ST表预处理, O ( 1 ) O(1) O(1)查询区间最值可以做到 O ( q n 2 + n l o g n ) O(qn^2+nlogn) O(qn2+nlogn)。
期望得分: 0 0 0
算法2:
我们发现查询一个区间时,其中每个满足所有元素都在 [ x , y ] [x,y] [x,y] 内的 极长子区间 的贡献为 t ( t + 1 ) 2 \frac{t(t+1)}{2} 2t(t+1), 其中 t t t 为该子区间的长度。
于是对于每次询问时扫描出所有极长子区间即可做到 O ( q n ) O(qn) O(qn)。
期望得分: 0 0 0 ~ 34 34 34
(虽然构造数据卡了,但还是被一位小常数玩家卡过去了第一档分)
算法3:
很容易想到要使用数据结构维护,于是我们想到了莫队,但是 值域 [ x , y ] [x,y] [x,y] 一直在变,似乎很难维护,所以我们选择转变思路。
由于题目给的序列时一个排列,所以我们考虑在值域上莫队,每次移动只会改变一个位置是否有效。然后考虑维护答案,很显然可以线段树。每个节点维护一下
左、右端开始的极长有效区间长度,区间答案,与区间是否全部有效即可。
时间复杂度 O ( n q l o g n ) O(n\sqrt{q}\ logn) O(nq logn)
期望得分: 34 34 34
算法4:
考虑另一种 O ( q n ) O(qn) O(qn)的做法,每次询问扫描值域,判断值域内每个值是否在区间内,在就更新答案。更新答案时需要用链表维护一下每个极大有效区间。
期望的分 0 0 0
算法5:
发现时间复杂度的瓶颈在于查询时需要线段树,考虑优化。
所以我们对序列分块,结合算法四的方法,每个块内单独维护一个链表,和区间的答案,由于维护的信息难以撤销,所以我们选择回滚莫队维护值域,区间长度小于等于 2 n 2\sqrt{n} 2n的使用算法4暴力,大于的部分每次 O ( n ) O(\sqrt{n}) O(n)查询,每次莫队移动时 O ( 1 ) O(1) O(1)维护所在块的链表,回滚时使用时间戳 O ( 1 ) O(1) O(1)回溯。
总体时间复杂度 O ( n n ) O(n\sqrt{n}) O(nn),代码量4k左右。
期望得分: 100 100 100
总体来说这是一道简单的题,解法自然,码量适中,思维难度适中,考察了线段树、莫队、分块、链表等多种初等数据结构,是一道不折不扣的小清新数据结构好题。
附std:
#include
using namespace std;
typedef long long LL;
LL que_range( int x ) { return 1ll * x * (x + 1) / 2; }
template<int _size>
struct stamp{
int A[ _size ];
int B[ _size ];
int T[ _size ];
int cnt = 1;
bool bind = false;
void Roll() { cnt ++; }
void Bind() { bind = true; Roll(); }
void Unte() { bind = false; }
void Set0() { memset( B, 0, sizeof(B) ); Roll(); }
int& operator []( int index ) {
if( bind ) {
if( T[index] ^ cnt ) T[index] = cnt, A[index] = B[index];
return A[index];
} else return B[ index ];
}
};
int block = 500;
int belong[200005];
int beg[405];
struct que{
int l, r, x, y, id;
que() {}
que( int _id ) { cin >> l >> r >> x >> y; id = _id; }
bool operator <( que& from ) const
{ return belong[x] == belong[ from.x ] ? y < from.y : x < from.x; }
};
int n, q;
vector<que> Q;
int c [200005];
int uc[200005];
stamp<200005> pre;
stamp<200005> nxt;
stamp<405> mid;
int bf1( que& x ) {
int ans = 0, lst = 0;
for( int i = x.l; i <= x.r; i ++ ) {
if( c[i] <= x.y and c[i] >= x.x ) lst ++;
else lst = 0;
ans += lst;
}
return ans;
}
int bf2( que& x ) {
int ans = 0;
for(int i = x.x;i <= x.y;i ++) {
int p = uc[i];
if( p < x.l or p > x.r ) continue;
ans -= que_range( p - pre[p - 1] );
ans -= que_range( nxt[p + 1] - p );
nxt[ pre[p - 1] ] = nxt[p + 1];
pre[ nxt[p + 1] ] = pre[p - 1];
ans += que_range( nxt[p + 1] - pre[p - 1] + 1 );
}
pre.Roll(); nxt.Roll();
return ans;
}
void updata( int x ) {
int p = uc[x], b = belong[p];
if( p ^ beg[b] ) {
if( pre[p - 1] != beg[b] ) mid[b] -= que_range( p - pre[p - 1] );
if( p != beg[b + 1] - 1 )nxt[ pre[p - 1] ] = nxt[p + 1];
else nxt[p] = p, pre[p] = pre[p - 1], nxt[ pre[p - 1] ] = p;
}
if( p ^ beg[b + 1] - 1 ) {
if( nxt[p + 1] != beg[b + 1] - 1 ) mid[b] -= que_range( nxt[p + 1] - p );
if( p != beg[b] ) pre[ nxt[p + 1] ] = pre[p - 1];
else pre[p] = p, nxt[p] = nxt[p + 1], pre[ nxt[p + 1] ] = p;
}
if( p != beg[b] and p != beg[b + 1] - 1 and pre[p - 1] != beg[b] and nxt[p + 1] != beg[b + 1] - 1 )
mid[b] += que_range( nxt[p + 1] - pre[p - 1] + 1 );
}
LL ans[200005];
int main(){
cin >> n >> q;
for(int i = 1;i <= n;i ++) {
cin >> c[i]; uc[ c[i] ] = i;
belong[i] = i / block + 1;
if( belong[i] > belong[i - 1] ) beg[ belong[i] ] = i;
}
if( beg[ belong[n] ] == n ) belong[n] --;
beg[ belong[n] + 1 ] = n + 1; belong[ n + 1 ] = belong[n] + 1;
for(int i = 0;i <= n + 1;i ++) pre[i] = i + 1, nxt[i] = i - 1;
nxt.Bind(); pre.Bind(); mid.Bind();
for(int i = 1;i <= q;i ++) {
que x = que(i);
if ( x.r - x.l <= block * 2 ) ans[i] = bf1( x );
else if( x.y - x.x <= block * 2 ) ans[i] = bf2( x );
else Q.push_back( x );
}
sort( Q.begin(), Q.end() );
int pt = 0;
for(int i = 1;i < belong[n];i ++) {
int r = beg[i + 1] - 1;
nxt.Unte(); pre.Unte(); mid.Unte();
for(int j = 0;j <= n + 1;j ++) pre[j] = j + 1, nxt[j] = j - 1; mid.Set0();
while( pt < Q.size() and belong[ Q[pt].x ] == i ) {
auto &P = Q[pt];
nxt.Unte(); pre.Unte(); mid.Unte();
while( r < P.y ) updata( ++ r );
nxt.Bind(); pre.Bind(); mid.Bind();
for( int j = P.x; j < beg[i + 1]; j ++ ) updata( j );
long long Ans = 0, lst = 0;
int bl = belong[ P.l - 1 ] + 1;
int br = belong[ P.r + 1 ] - 1;
for( int j = P.l; belong[j] < bl; j ++ ) {
if( c[j] >= P.x and c[j] <= P.y ) lst ++;
else Ans += que_range( lst ), lst = 0;
}
for( int j = bl; j <= br; j ++ ) {
if( nxt[ beg[j] ] == beg[j + 1] - 1 ) lst += beg[j + 1] - beg[j];
else {
lst += nxt[ beg[j] ] - beg[j] + 1;
Ans += que_range(lst); Ans += mid[j];
lst = beg[j + 1] - pre[ beg[j + 1] - 1 ];
}
}
for( int j = beg[br + 1]; j <= P.r; j ++ ) {
if( c[j] >= P.x and c[j] <= P.y ) lst ++;
else Ans += que_range( lst ), lst = 0;
}
Ans += que_range( lst );
ans[ P.id ] = Ans;
nxt.Roll(); pre.Roll(); mid.Roll();
pt ++;
}
}
for(int i = 1;i <= q;i ++) cout << ans[i] << "\n";
return 0;
}