这真的是一道很神的题,但在CODEVS。。可以暴力过掉。。当然,在BZOJ上是不可以的。
所以。。我还是看了看题解,题解是这样说的:
一、一个基本的转化是将题目中的描述翻译为一个长度为3的等差子序列,即存在x,k,使得x-k与x+k在x异侧。
二、我们先来看一个错误的贪心思路,因为是一个1~N的排列,所以我们可以把它们视为离散后的数据,首先我们将其按奇偶分开,奇数放一边,偶数放一边,这样就可以保证不会有跨两侧的等差子序列,然后我们再将两侧的子序列离散,以类似的思路令其也符合上述要求,那么我们就可以得到一个没有等差子序列的排列。
然后我们再按照上述思路逆check即可。
但问题是,其逆命题真的成立么?难道就不可能有不符合上述要求的排列,但它之中也不存在等差子序列么?
答案是否定的!
一个显然的例子是2、1、4、3;
如果你认为这是因为它长度为4的话,那么下面给出一个长度为6的反例:
5,6,1,3,2,4
但是这种贪心对于随机数据的正确率是相当高的,事实上,BZOJ的数据只能卡掉其在4、5时的贪心,所以如果用这种思路写的话。。只要把4、5时的不为等差子序列的情况预先打个表就可以AC了。
但这种下流的做法显然不是我们想要的。。So。。
四、让我们来看看正解吧!
一个非常显然的思路在①中已经给出,笔者看到本题的时候也正是如此写的暴力,但这种暴力不可以说真正意义上A掉这道题的,不过呢,其实我们可以对它进行改进。
考虑如何对一个数x以极低的复杂度判断其x-k与x+k,k∈(0,min(x-1,N-x))是否在x两侧?
①换个角度考虑,两侧的反面就是一侧,即它们在x的左侧或右侧,这有什么好处呢?
假如我们用一个布尔数组来记录每一个数x的出现情况的话,那么我们就可以离线地从左到右扫描每一个数x,判断从x-1到最左边与从x+1到最右边是否一样即可,如果出现不同了,就说明出现了等差子序列。如果相同的话,就意味着以x为中项的等差子序列是不存在的,因为其可能的首项和末项要么都在其之前出现了(均为1),要么都在其之后出现了(均为0)。
于是我们发现,我们可以用两个二进制数来表示x-k与x+k的出现情况,然后check这两个二进制数。
②但是N有10000呢,太大了!这可怎么办?Hash!最简单的hash就好了,我们可以保存一个01串,然后需要的时候计算它的二进制值,在计算的时候模一个大质数就好了。——补充,多年后——其实还有一种更好的方法,就是bitset!比较一下时间复杂度bitset:O(TN^2/100)≈7*10^6,线段树:O(TNlogN)≈9*10^5+大常数。妈蛋似乎还是差了很多,但是这道题N这么小,bitset完全没有问题啊。
③但是。。这样的复杂度依然是O(N^2T)≈10^9的,这时我们发现我们其实是在对一个线段求hash,而根据我们的方法,两个线段的hash值是可以合并的,于是这个问题满足分治解决的要求,那么我们是完全可以为原先的扫描线配上线段树以加速hash过程的,于是正解便呼之欲出了!
综,本题的正解就是扫描线+线段树+hash!
五、很容易犯错的地方:
在询问的时候,合并线段信息一定要头脑清晰,
①是正着来还是倒着来别搞混了。
②当询问落到这个线段树的某个节点的时候,询问区间分为在它的左儿子、在它的右儿子、在它的左右儿子三种情况,最好是分开写比较条理清晰,我想把它们合并起来结果一不小心就蛋痛了。。
暴力:
#include<iostream>
using namespace std;
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdio>
char * ptr=new char[1000000];
inline void in(short &x){
while(*ptr<'0'||*ptr>'9')++ptr;
x=0;
while(*ptr>47&&*ptr<58)x=x*10+*ptr++-'0';
}
int main(){
short T,n,i,j,a[10001],tmp,k;
fread(ptr,1,1000000,stdin);
in(T);
while(T--){
in(n);
for(i=0;i<n;++i){
in(tmp);
a[tmp]=i;
}
bool flag=0;
for(i=2;i<n;++i)
for(j=i,k=i;--j&&++k<=n;)
if(a[j]>a[i]){
if(a[i]>a[k]){
flag=1;
break;
}
}
else
if(a[j]<a[i])
if(a[i]<a[k]){
flag=1;
break;
}
if(flag)printf("Y\n");
else printf("N\n");
}
}
贪心:
#include<iostream>
using namespace std;
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
int a[10000],tmpsort[10000],p[10000],N,tmpwork[10000];
bool flag4[4000],flag5[50000];
char * ptr=(char *)malloc(1000000);
inline void in(int &x){
while(*ptr<'0'||*ptr>'9')++ptr;
x=0;
while(*ptr>47&&*ptr<58)x=x*10+*ptr++-'0';
}
inline int getint(int * a,int l,int r){
int x=0;
for(int i=l;i<=r;++i)x=x*10+a[i];
return x;
}
inline bool check(int l,int r){
//printf("%d,%d\n",l,r);
if(r-l<2)return 1;
int tot=0,i=l;
for(;i<=r;++i)tmpsort[tot++]=a[i];
sort(tmpsort,tmpsort+tot);
for(i=0;i<tot;++i)p[tmpsort[i]]=i;
for(i=l;i<=r;++i)tmpwork[i]=p[a[i]];
if(r-l+1==4)return flag4[getint(tmpwork,l,r)];
if(r-l+1==5)return flag5[getint(tmpwork,l,r)];
for(i=l;i<=r;++i)tmpwork[i]=tmpwork[i]&1;
if(tmpwork[l]==tmpwork[r])return 0;
int m=(r-l+1)>>1;
for(i=1;i<m;++i){
//cout<<l<<","<<r<<":"<<l+i<<endl;
if(tmpwork[l]!=tmpwork[l+i])
return 0;
}
for(i=1;i<m;++i){
//cout<<l<<","<<r<<":"<<r-i<<endl;
if(tmpwork[r]!=tmpwork[r-i])
return 0;
}
m=(l+r)>>1;
if(tmpwork[m]!=tmpwork[l])--m;
//cout<<l<<":"<<tmpwork[l]<<"("<<a[l]<<") "<<m<<":"<<tmpwork[m]<<"("<<a[m]<<")\n",
return check(l,m)&&check(m+1,r);
}
inline bool check(){
for(int i=0;i<N;++i)
for(int j=i+1;j<N;++j)
for(int k=j+1;k<N;++k)
if(a[k]-a[j]==a[j]-a[i])
return 0;
return 1;
}
int main(){
int T;
//-------pre-work--------
for(int i=0;i<5;++i)a[i]=i;
N=4;
while(next_permutation(a,a+N))
if(check())
flag4[getint(a,0,N-1)]=1;
N=5;
while(next_permutation(a,a+N))
if(check())
flag5[getint(a,0,N-1)]=1;
//-------work-----------
return 0;
fread(ptr,1,1000000,stdin);
in(T);
while(T--){
in(N);
for(int i=0;i<N;++i)in(a[i]);
if(check(0,N-1))printf("N\n");
else printf("Y\n");
}
}
线段树:
#include<iostream>
using namespace std;
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define root 1,1,N
#define lson node<<1,l,(l+r)>>1
#define rson node<<1|1,((l+r)>>1)+1,r
#include<bitset>
const int P=100000007;
typedef long long lld;
lld tree[40000][2],mi[10001];
void update(int node,int l,int r,int x){
if(l==r){
tree[node][0]=1;
tree[node][1]=1;
}
else{
int m=(l+r)>>1;
if(x>m)update(rson,x);
else update(lson,x);
tree[node][1]=(tree[node<<1][1]+tree[node<<1|1][1]*mi[((l+r)>>1)-l+1]%P)%P;
tree[node][0]=(tree[node<<1|1][0]+tree[node<<1][0]*mi[r-((l+r)>>1)]%P)%P;
}
}
lld query(int node,int l,int r,int a,int b,int x){
if(l==a&&r==b)return tree[node][x];
int m=(l+r)>>1;
lld left=0,right=0;
if(m<b)right=query(rson,max(m+1,a),b,x);
if(a<=m)left=query(lson,a,min(m,b),x);
return (x?left+right*mi[max(0,m-a+1)]%P:right+left*mi[max(0,b-m)]%P)%P;
}
int main(){
int T,i,x,len,N;
lld tmp1,tmp2;
mi[0]=1;
for(i=1;i<10001;++i)mi[i]=(mi[i-1]<<1)%P;
scanf("%d",&T);
while(T--){
scanf("%d",&N);
memset(tree,0,sizeof(tree));
for(i=0;i<N;++i){
scanf("%d",&x);
len=min(x-1,N-x);
if(len&&query(root,x-len,x-1,0)!=query(root,x+1,x+len,1)){
printf("Y\n");
break;
}
update(root,x);
}
if(i==N)printf("N\n");
for(++i;i<N;++i)scanf("%*d");
}
}