笔者最近意外的发现 笔者的我的网站 http://tiankonguse.com/ 的不少文章被其它网站转载,可是转载时未声明文章来源或参考自 http://tiankonguse.com/ 网站,所以,笔者添加此条声明。php
郑重声明:这篇记录《【百度之星2014~初赛(第二轮)解题报告】JZP Set》转载自 http://tiankonguse.com/ 的这条记录:http://tiankonguse.com/record/record.php?id=668html
最近要毕业了,有半年没作比赛了.
此次参加百度之星第二轮娱乐一下.
如今写一下 JZP Set 这道题的的解题报告.ios
题意是:给你n个数(1到n),给你一个规则,问用这个规则能够获得多少个合法的集合.函数
具体规则是:一个合法集合里任意挑两个数,若是这两个数之和是偶数,这个偶数除以2获得的数也要在这个合法集合里.网站
好比: 3 和9 在集合里,3+9是偶数,因此 (3+9)/2 = 6 也要在这个集合里.而后 {3,6,9}就是一个合法的集合.spa
起初我理解错题意了,觉得是任意的两个数字之和除以2.好比 (3 + 6)/2 = 4, 我觉得4也要在集合里.htm
因而很快获得一个 (n+1)*n/2 的公式.blog
后来学弟告诉我正确的题意.递归
这个题的样例有10^5个,每一个样例最大是10^7.ci
首先来看看我理解错误题意时,是怎么作的.
假设 F(n) 是 n 的答案的话, 则 F(n + 1) = F(n) + f(n+1).
f(n+1)里面确定有 n+1, 我假设f(n+1) 这些集合的任一个集合 S 里的最小值是 a, 则 (a+n+1)/2 确定在 S 里面,而后这样递归下去发现 a到n+1的全部数字都必须在 S 里面.
因而 f(n+1) 就是 n+ 1 了.
因而最终方程就是 F(n+1) = F(n) + n+ 1.
只惋惜这是错误的题意的作法.
学弟告诉我正确的题意,仍是能够很快写出方程来
F(n+1) = F(n) + f(n + 1).
其中f(n+1) 是含有 n+ 1的合法的集合.
首先对于我上面找到的那些确定是合法集合的一部分,只是我遗漏了一些合法集合.
遗漏的确定是非连续的了.
而后很快能够想到 一个奇数和一个偶数构成的集合都是合法集合.
因而 f(n + 1) 就又加上含有一奇一偶的合法集合的数量了,只是提交后WA了.
而后学弟随手写了一个集合 {3, 6, 9 } 发现也是合法集合.
而后我无心见把12添加进去后发现仍是合法集合.
因而咱们得出结论:等差为奇数的数列都是合法集合,对于连续的那个只不过是等差为1罢了.
因而咱们假设 i/x 的个数为 num(i/x),
因而有
则最终答案是
C( num (n / 1) , 2) + C( num (n / 3) , 2) + C( num (n / 5) , 2) + ...
也就是等差位 x 的数列里,咱们随便挑一段都是合法集合.
只是到这一步咱们发现这样作仍是超时.
因为F(n+1) 与 F(n) 有很大的关系,因此咱们尝试找找递推行不行.
仍是这个公式
F(n) = F(n-1) + f(n).
f(n) 表明含有 n 的合法集合.
而后这些集合须要全是等差数列.
因而写了这么一个公式
f(n-1) =
n/1 + n/3 + n/5 + ....
而后就没什么想法了.
今天看了这个解题报告才知道能够这样作.
发现咱们若是写出 f(n-2) 那一项的公式
(n-1)/1 + (n-1)/3 + (n-1)/5 + ...
这两个公式大部分项是相等的,只有个别的几个不相等.
本身举了几个例子发现 n 整除 下面的项时,这个除式会多一个.
好比 n = 12 则 12/3 = 4, 11/3 = 3.
这样这个f(n)函数就能够转化为
f( n ) = f( n - 1) + Count( n -1 ).
其中 Count( n ) 表明 n 的奇数约数个数.
而后小舟学长的模板上恰好有求关于小于 n 的全部数的约数的个数.其中有 O( n*log(n) ) 的模板,也有 O( n ) 的模板,
因为 n*log(n) 的模板比较简单,也能够过这道题,因而我就是用 n*log(n) 的模板了.
/************************************************************************* > File Name: 4.2.cpp > Author: tiankonguse > Mail: i@tiankonguse.com > Created Time: Mon 26 May 2014 01:06:18 PM CST ***********************************************************************/ #include<cstdio> #include<cstdlib> #include<cstring> #include<iostream> #include<string> #include<queue> #include<map> #include<cmath> #include<stack> #include<algorithm> #include<functional> #include<stdarg.h> using namespace std; #ifdef __int64 typedef __int64 LL; #else typedef long long LL; #endif const int N = 10000010; LL nod[N]; LL count[N]; LL ans[N]; void __sieve_nod() { memset(nod, 0,sizeof(nod)); for (int i = 1; i < N; i+=2) { for (int j = i; j < N; j += i) { ++nod[j]; } } } void init(){ ans[0] = 1; ans[1] = 2; LL count = 1; for(int i=2;i<N;i++){ count += nod[i-1]; ans[i] = ans[i-1] + count; } } int main() { __sieve_nod(); init(); int t,n; scanf("%d",&t); for(int i=1; i<=t; i++) { scanf("%d",&n); printf("Case #%d:\n%I64d\n",i,ans[n]); } return 0; }