[CODEVS1283]等差子序列解题报告

23 篇文章 0 订阅
11 篇文章 0 订阅

这真的是一道很神的题,但在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");
	}
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值