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个点头盾,以为能上第一,谁知道上了第二。代码还是发了吧。。。。
首先要明确等差数列的定义。
一个等差数列可以用首项、公差和项数的三元组 (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; }
相关文章推荐
- 51nod 1056 最长等差数列 V2
- [51nod 1056]最长等差数列V2
- 【51nod 1055 & 1056】最长等差数列及V2 题解
- 1056 最长等差数列 V2
- 51 nod 1056 最长等差数列 V2
- 51nod-【1089 最长回文子串 V2(Manacher算法)】
- 【51Nod】1055 最长等差数列 动态规划
- 【51NOD-0】1089 最长回文子串 V2(Manacher算法)
- 51Nod - 1089 最长回文子串 V2(Manacher算法)
- 51nod 1055 最长等差数列
- 51Nod 1089 最长回文子串 V2(Manacher算法)
- 51nod-1089 最长回文子串 V2(Manacher算法)
- 51Nod 1089 最长回文子串 V2(Manacher算法)
- 51nod 1089 最长回文子串 V2 (Manacher算法)
- 51Nod-1089 最长回文子串 V2(Manacher算法)
- 【51nod】1089 最长回文子串 V2(Manacher算法)
- 51nod 1055 最长等差数列
- 51nod 1089 最长回文子串 V2(Manacher算法)
- 51nod 1241 特殊的排序(DP_找最长等差数列)
- 51nod1089---最长回文子串 V2(51nod基础:Manacher算法)