题目连接:
题目大意:
给出一个序列,m次查询,每次给出一个子串,问这个子串是否满足,中间能够找到一个元素,让这个元素作为前后分别单调的分界.
题目分析:
- 首先对于每次查询,我们知道分界一定是最大的元素,所以我们可以用rmq预处理出区间最大。
- 然后为了判断这个区间是否能够通过最大的元素作为分界点而前后单调,我们可以通过预处理,正向和反向分别扫一遍,记录某一个点的为最大元素的能够向左和向右得到的最长的单调子串的长度,然后每次只需要O(1)的判断就可以,判断当前元素最为最大的元素延展的最大的子串是否覆盖当前查询的子串。如果是,那么结果就是YES,否则是No
AC代码:
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#define MAX 100007
using namespace std;
typedef pair<int,int> PII;
int n,m,a[MAX],lef[MAX],rig[MAX],dp[MAX][30],ans[MAX][30];
void make ( )
{
for ( int i = 1 ; i <= n ; i++ )
{
dp[i][0] = a[i];
ans[i][0] = i;
}
for ( int j = 1 ; (1<<j) <= n ; j++ )
for ( int i = 1 ; i+(1<<j)-1 <= n ; i++ )
{
dp[i][j] = max ( dp[i][j-1] , dp[i+(1<<(j-1))][j-1]);
if ( dp[i][j-1] == dp[i][j] )
ans[i][j] = ans[i][j-1];
else ans[i][j] = ans[i+(1<<(j-1))][j-1];
}
for ( int i = 1 ; i <= n ; i++ )
lef[i] = rig[i] = 1;
for ( int i = 2 ; i <= n ; i++ )
if ( a[i-1] <= a[i] )
lef[i] = lef[i-1]+1;
for ( int i = n-1 ; i >= 1 ; i-- )
if ( a[i+1] <= a[i] )
rig[i] = rig[i+1]+1;
}
PII query ( int l , int r )
{
int k = (int)((log((r-l+1)*1.0))/log(2.0));
int maxn;
int temp;
maxn = max ( dp[l][k] , dp[r-(1<<k)+1][k] );
if ( maxn == dp[l][k] ) temp = ans[l][k];
else temp = ans[r-(1<<k)+1][k];
return make_pair ( maxn , temp );
}
int main ( )
{
while ( ~scanf ( "%d%d" , &n , &m ))
{
for ( int i = 1 ; i <= n ; i++ )
scanf ( "%d" , &a[i] );
make();
while ( m-- )
{
int l,r;
scanf ( "%d%d" , &l , &r );
PII temp = query ( l , r );
int x = temp.second;
//cout << x << " " << lef[x] << " " << rig[x] << endl;
if ( x-lef[x]+1 <= l && x+rig[x]-1 >= r )
puts ( "Yes" );
else puts ("No");
}
}
}