[BZOJ2124]等差子序列/[CF452F]Permutation
题目大意:
一个(1sim n)的排列(A_{1sim n}),询问是否存在(i,j(i<j)),使得(A_i<A_j)且(frac{A_i+A_j}2)在(i,j)之间出现。
BZOJ上的数据范围:(nle10000);
CF上的数据范围:(nle3 imes10^5)。
思路:
从左到右枚举每一个数,用两个布尔数组(b_0,b_1)分别维护数值为(i)的数是否在当前数的左边、右边出现。然后将与当前数差值相等的位置对应起来(如,当前(A_i=3)时,将(b_{0,1})与(b_{1,5})对应起来),看一下对应位置有没有都是(1)的,如果有,则说明存在。
使用bitset
优化可以做到(mathcal O(frac{n^2}{32})),但还是过不了。
发现如果只用一个数组(b)维护左边出现过的数,那么对于当前位置(i),若以(b_{A_i})为中心的极大字符串是不是回文串,说明一个在左边出现,一个在右边出现,那么一定存在解。而确定中心的回文串判定可以用树状数组维护哈希实现。
事件复杂度(mathcal O(nlog n))。
源代码:
#include<cstdio>
#include<cctype>
#include<algorithm>
inline int getint() {
register char ch;
while(!isdigit(ch=getchar()));
register int x=ch^'0';
while(isdigit(ch=getchar())) x=(((x<<2)+x)<<1)+(ch^'0');
return x;
}
const int N=300001;
const unsigned base=13;
unsigned pwr[N];
int n;
class FenwickTree {
private:
unsigned val[N];
int lowbit(const int &x) const {
return x&-x;
}
unsigned query(const int &p) const {
unsigned ret=0;
for(register int i=p;i;i-=lowbit(i)) {
ret+=val[i]*pwr[p-i];
}
return ret;
}
public:
void modify(const int &p) {
for(register int i=p;i<=n;i+=lowbit(i)) {
val[i]+=pwr[i-p];
}
}
unsigned query(const int &l,const int &r) const {
return query(r)-query(l-1)*pwr[r-l+1];
}
};
FenwickTree t[2];
int main() {
n=getint();
for(register int i=pwr[0]=1;i<=n;i++) {
pwr[i]=pwr[i-1]*base;
}
bool ans=false;
for(register int i=1;i<=n;i++) {
const int x=getint();
const int len=std::min(x-1,n-x);
ans|=t[0].query(x-len,x-1)!=t[1].query(n-x-len+1,n-x);
t[0].modify(x);
t[1].modify(n-x+1);
}
puts(ans?"YES":"NO");
return 0;
}