【洛谷P1214】等差数列

题目大意:列出从一个给定上界的双平方数集合中选出若干个数,组成长度为 N 的等差数列的首项和公差。

题解:首先,因为是在双平方数集合上的等差数列,而且根据题目范围可知,上界不超过 2e5,可以先打表,将符合条件的双平方数存入一个数组,并排序离散化。在等差数列中,只要数列中的前两项确定,整个数列就会被确定下来。因此,考虑枚举序列的前两项,至此时间按复杂度为 (O(cnt^2))。接着,若直接枚举后面的项来判断是否长度可以到达 N 的话,时间复杂度还要多一个 cnt 的乘积因子,难以承受。其实,在枚举的过程中,产生了大量的无效结果,最后才判断导致时间复杂度暴增,同时根据双平方数集合不大,可以直接用桶来装,因此,可以直接枚举 N,每次判断数列的下一项是不是在双平方数集合中即可,时间复杂度为 (O(cnt^2*N))

代码如下

#include <bits/stdc++.h>
using namespace std;
const int maxn=150000;

int n,m,c[maxn],cnt;
bool in[maxn];
struct node{
	int a,d;
}ans[maxn];
int tot;

bool cmp(const node& x,const node& y){
	if(x.d^y.d)return x.d<y.d;
	return x.a<y.a;
}

void read_and_parse(){
	scanf("%d%d",&n,&m);
	for(int i=0;i<=m;i++)
		for(int j=0;j<=m;j++)
			c[++cnt]=i*i+j*j,in[c[cnt]]=1;
	sort(c+1,c+cnt+1);
	cnt=unique(c+1,c+cnt+1)-c-1;
}

void solve(){
	for(int i=1;i<=cnt-n+1;i++)
		for(int j=i+1;j<=cnt-n+2;j++){
			int d=c[j]-c[i],len=2,now=c[j],flag=1;
			if(c[i]+(n-1)*d>c[cnt])break;
			while(len<n){
				++len,now+=d;
				if(!in[now]){flag=0;break;}
			}
			if(flag)ans[++tot]=node{c[i],d};
		}
	if(!tot)puts("NONE");
	else{
		sort(ans+1,ans+tot+1,cmp);
		for(int i=1;i<=tot;i++)printf("%d %d
",ans[i].a,ans[i].d);
	}
}

int main(){
	read_and_parse();
	solve();
	return 0;
}