[HDU6304][數學] Chiaki Sequence Revisited-杭電多校2018第一場G


[HDU6304][數學] Chiaki Sequence Revisited

-杭電多校2018第一場G


題目描述

現在拋給你一個數列\(A\)

\[a_n=\begin{cases}1 & n = 1,2 \\ a_{n - a_{n-1}} + a_{n-1 - a_{n-2}} & n \ge 3\end{cases} \]

現在需要你計算它的前綴和 \(\sum\limits_{i=1}^{n}a_i \ mod \ (10^9+7)\)

數據范圍 \(n(1\le n\le 10^{18})\)

題目分析

不可描述的做法

拿到題目第一步對序列\(A\)打一個100的表,對吧,然后 "OEIS" 一下,發現真的有

http://oeis.org/A046699

\(a[1..] = {1,2,2,3,4,4,4,5,6,6,7,8,8,8,8,9,....}\)

結果發現並沒有什么用,既沒有通項公式,更別說求和公式了。

大概正確的規律

可以發現每種數字的出現次數是由規律可循的,\(1\)出現了\(1\)次,\(2\)出現了\(2\)次,\(3\)出現了\(1\)

我們設\(f(x)\)代表數字\(x\)出現的次數

那么 \(f[1..] = {1,2,1,3,1,2,1,4,1,2,1,3,1,2,1,5,....}\)

通過觀察100項的表可以的出一個大概正確的規律,數字\(x\)會出現\(1+log_2lowbit(x)\)

至於\(lowbit(x)\) 是啥,可以去了解一下樹狀數組,表示 \(\min\{2^k:2^k|x\}\)

經過總結規律可以發現遞推式,那么有

\[\begin{cases} {f(2k+1) =1 }\\\\f(2k)=f(k)+1\end{cases} \]

也就是 http://oeis.org/A001511 中提到的數列,但是知道出現次數並沒有什么用,我們需要計算的是\(f(x)\)

的前綴和,這樣我們就可以知道\(n\)位置上表示的數字的值,即\(a_n\)的值。

我們設 $g(n) = \sum^{n}_{i=1}f(i) $ ,可以簡單推導一下

\[\begin {align} &g(n) = \sum_{k=1}^{n}f(k) \\ &g(n) = \sum^{\lfloor \frac{n-1}{2}\rfloor}_{k=0}f(2k+1) + \sum^{\lfloor \frac{n}{2}\rfloor}_{k=1}f(2k)\\ &g(n) = \Big\lceil\frac{n}{2}\Big\rceil + \sum^{ \lfloor \frac{n}{2}\rfloor}_{k=1} \{f(k)+1\}\\ &g(n) = \Big\lceil\frac{n}{2}\Big\rceil + \Big\lfloor\frac{n}{2}\Big\rfloor + g(\Big\lfloor\frac{n}{2}\Big\rfloor) \\ &g(n) = g(\Big\lfloor\frac{n}{2}\Big\rfloor)+n \\ \end {align} \]

也就是說我們現在可以在\(O(\log N)\)時間計算出\(g(n)\) ,由於該函數顯然是單調的,那么我們現在可以通過二分求得\(a_n\)對應的值,即 \(a_n = \min\{k\ |\ g(k)\ge n\}\)
然而題目要我們求前綴和,那么問題來了,我們現在計算單個值就需要\(O(\log^2N )\)時間
如何計算出前綴和呢?考慮通過每個數字的出現次數入手。

\[\begin{matrix} 1, 3, 5, 7, 9, \cdots ,2(t-1)+1 &\quad\quad\text{分別出現一次} \\ 2,6,10,14,18, \cdots ,4(t-1)+2 &\quad\quad\text{分別出現兩次} \\ 4,12,20,28,36,\cdots,8(t-1)+4 &\quad\quad\text{分別出現三次} \\ \vdots &\vdots\\ \cdots 2^k(t-1)+2^{k-1} &\quad\quad\text{分別出現$k$次} \end{matrix} \]

由於每一行都相當於一個等差數列,現在的目標就是找到每一行的末項就好了。
也就是找到__最后一個小於\(a_n\)的值__,再用等差數列求和公式\(O(1)\)計算出每一行的值,最后所有行加起來就是答案的主要部分了。

會發現經過上面的計算所有等於\(a_n\)的項沒有計算入答案,我們只要計算出等於\(a_n\)的有多少項,最后再累加到答案,這道題就做完了。容易得到 \(a_n\)需要計算\(n-g(a_n-1)\)次。根據最開始我對數列的偏移,正確的答案還需要再+1。整體復雜度為\(O(\log^2N+logN)\)

注:計算\(a_n\)需要 \(O(\log^2N)\)時間,需要估計二分上下界,否則會超時。

無比准確的題解

以下是多校官方給的題解。

考慮這個數列的差分數列,除了個別項,本質就是:\(1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0,...\)

可以觀測到,這個序列可以這么生成:一開始只有一個\(1\)\(1\)變成\(110\)\(0\)保持不變。迭代無窮多次后就是這個差分序列。

知道差分序列,可以應用阿貝爾變換,把\(a\)的前綴和搞成差分序列相關。不妨令差分序列是\(da\),那么\(a\)的前綴和$$s(n)=(n-1)\sum_{i=0}^{n-2}da(i) - \sum_{i=0}^{n-2}da(i)i + 1$$。

利用\(da\)的分形結構,很容易算出\(s(n)\)

代碼Code

/*
[HDU6304][數學] 
Chiaki Sequence Revisited
*/
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MOD = 1e9+7;
const int inv2 = 500000004;
int T;
LL n;
LL calc(LL n) {
	if(n<=1) return n;
	else return calc(n/2)+n;
}
void solve() {
	LL l=n/2-30,r=n/2+30,m,p=-1;
    //需要預先估計上下界減少二分次數,否則會TLE.
	while(l<=r) {
		m = (l+r)/2;
		if(calc(m)>n) r=m-1;
		else l=m+1,p=m;
	}
	LL rest = ((n - calc(p))%MOD+MOD)%MOD;
	LL ans = 0, s, t, e, k, c=1, x, y;
	for(LL i=1;; i<<=1,c++) {
		if(i>p) break;
		x = i%MOD;
		y = 2*i%MOD;
		s = x;
		k = ((p-i)/(2*i)+1)%MOD;
		e = (y*(k-1)%MOD+i)%MOD;
		ans = (ans+c*(s+e)%MOD*k%MOD*inv2%MOD)%MOD;
	}
	ans = (ans + rest*((p+1)%MOD)%MOD)%MOD;
	printf("%lld\n",ans+1);
}
int main() {
	scanf("%d",&T);
	while(T--) {
		scanf("%lld",&n);
		n--; //偏移一項
		solve();
	}
	return 0;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM