您的位置:首页 > 其它

51nod 1056 最长等差数列 V2

2017-10-03 16:51 274 查看
这个和1055那个题差不多,稍微改改就过了,首先是剪枝的改写,这个剪枝是必过的,唐老师在讨论里进行了证明。

首先要明确等差数列的定义。

一个等差数列可以用首项、公差和项数的三元组 (first,delta,length) 表示,当然这个 first 也可以换成末项 last ,取决于你的算法。

我们要考虑的等差数列要尽量用少的信息表示所有的可能,也就是说提取出来的等差数列之间不能有相互包含的关系,例如 (2,3,4) 和 (2,3,5) 就冗余了,而 (2,3,4) 和 (5,3,4) 也是冗余的,因为当它们出现在同一个序列里时, (2,3,5) 也是存在的。

因此我们只需要考虑满足 (first−delta) 这个数字不存在,并且 (last+delta) 这个数字不存在的等差数列。

接下来要发现这样的等差数列在 k 充分大时是不多的。

定义:集合里的两个元素是足够相邻的,当且仅当它们的排名之差不超过 nk−1 。由鸽巢原理可知,一个长度为 k 的等差数列必然包含至少 k−12 对足够相邻的元素(考虑等差数列里排名最小的和排名最大的那两个元素)。

而集合里足够相邻的元素对数不超过 n22(k−1) ,我们所求的不同等差数列也不会包含相同的一对足够相邻的元素,所以我们能得到的等差数列数量是不超过 n2(k−1)2 的。

最后要利用这个性质来构造相应的算法。

利用数量不多的性质,我们可以尝试找出所有长度至少为 200 的等差数列,从中选取最长的那个。

注意到上面的证明利用到了集合的大小,类似地也可以证明,这样的等差数列必然有 ⌊k2⌋ 项在前 ⌊n2⌋ 小的元素集合里,或者有 ⌊k2⌋ 项在前 ⌈n2⌉ 大的元素集合里。

而集合大小减半同时数列项数减半对数列数量的影响是不大的,所以可以尝试对集合分治,分别找出更小情况的解,然后放到大的集合里尝试向左向右 O(k) 扩张,再进行去重,从而得到当前情况的解。

不妨设 T(n,k) 表示从大小为 n 的集合里提取长度至少为 k 的不同等差数列的复杂度,则有 T(n,k)=2T(n2,k2)+O(n2k2k)⇒T(n,k)=O(n2klognk)。

其次,就是hash这个hash要手打,hash就是把之前的dp数组给换掉。暴力的去查找。最后,我想说的是,花了130个点头盾,以为能上第一,谁知道上了第二。代码还是发了吧。。。。

#include<cstdio>
#include<algorithm>
#define ll long long
using namespace std;
const int mod=233333;

int a[50010],ha[mod],ans=199,last[50010],n;
int read(){
char c=getchar();int k=0;for (;c<48||c>57;c=getchar());
for (;c>47&&c<58;c=getchar()) k=(k<<3)+(k<<1)+c-48;return k;
}
void add(int k,int i){
int x=k%mod;last[i]=ha[x];ha[x]=i;
}
bool find1(int k){
if (k>a
) return 0;
for (int i=ha[k%mod];i;i=last[i])
if (a[i]==k) return 1;
return 0;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
a[i]=read();
sort(a+1,a+n+1);
for(int i=1;i<=n;i++)add(a[i],i);
for(int i=2;i<=n-ans;i++)
{
int j=i-1,k=i+1;
while(j>0&&k<=n)
{
if(a[j]+a[k]>2*a[i])j--;
else if(a[j]+a[k]<2*a[i])k++;
else
{
ll d=a[i]-a[j];
ll maxn=d*(ll)ans+(ll)a[j];
if(maxn>a
)break;
int now=3,now1=a[k]+d;
for(;find1(now1);now1+=d)now++;
ans=max(ans,now);
j--;
k++;
}
}
}
//cout<<ans<<endl;
if(ans>=200)
printf("%d\n",ans);
else printf("No Solution\n");
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: