筆者最近意外的發現 筆者的我的網站 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; }