题目链接:
hdu: http://acm.hdu.edu.cn/showproblem.php?pid=5172
bc(中文):http://bestcoder.hdu.edu.cn/contests/contest_chineseproblem.php?cid=567&pid=1003
题解:
线段树+前缀和
一个区间要满足1到n的一个排列,要同时满足两点,一是它的和是n*(n+1)/2,这个可以用前缀和直接求;二是它每个元素不能重复。
区间内每个元素都不能重复:
记录第i个元素的左边一个离他最近的值与他相等的数的位置,记在arr[i]里面,对于区间[L,r]要满足区间里面最大的arr[i](L<=i<=r)要小于L;这个可以用线段树或rmq维护。
要先比较第一个条件,即和要先等于n*(n+1)/2,满足了再去查线段树,否则会超时。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define lson (o<<1)
#define rson ((o<<1)+1)
#define M (l+(r-l)/2)
using namespace std;
typedef long long LL; const int maxn = 1e6 + ;
LL sumv[maxn << ];
int maxv[maxn << ];
int arr[maxn],vis[maxn];
int n, m; int max(int a, int b) { return a > b ? a : b; } void build(int o, int l, int r) {
if (l == r) {
maxv[o] = arr[l];
}
else {
build(lson, l, M);
build(rson, M + , r);
maxv[o] = max(maxv[lson], maxv[rson]);
}
} int ql, qr;
void query(int o, int l, int r,int &ma) {
if (ql <= l&&r <= qr) {
ma = max(ma, maxv[o]);
}
else {
if (ql <= M) query(lson, l, M,ma);
if (qr > M) query(rson, M + , r, ma);
}
} bool check(int l,int r) {
LL nn = r - l + ;
if (nn*(nn + ) / != sumv[r] - sumv[l - ]) return false;
int ma = -;
query(, , n,ma);
if (ma >= l) return false;
return true;
} void init() {
sumv[] = ;
memset(vis, , sizeof(vis));
} int main() {
while(scanf("%d%d", &n, &m) == && n) {
init();
for (int i = ; i <= n; i++) {
int x;
scanf("%d", &x);
sumv[i] = sumv[i - ] + x;
arr[i] = vis[x];
vis[x] = i;
}
build(,,n);
while (m--) {
scanf("%d%d", &ql, &qr);
LL len = qr - ql + ;
if (check(ql,qr)) {
puts("YES");
}
else {
puts("NO");
}
}
}
return ;
}