1. 程式人生 > 實用技巧 >SP6286 SUMMUL - Sum of products 題解

SP6286 SUMMUL - Sum of products 題解

CSDN同步

原題連結

簡要題意:

\(T\) 組資料,每組資料給定一個正整數 \(n\),求 所有將 \(n\) 分解為至少 \(2\) 個正整數之和的乘積之和。(拆分順序不同也算方案不同)

例如 \(n=3\) 時,\(3 = 1 + 1 + 1 = 1 + 2 = 2+1\),所以答案為 \(1 \times 1 \times 1 + 1 \times 2 + 2 \times 1 = 5\). 答案對 \(10^9+7\) 取模。

資料範圍:\(T \leq 10^3 , n \leq 10^9\).

很顯然,考慮 \(f_i\) 表示 \(i\) 的答案。

貌似可以得到

\[f_i = \sum_{j=0}^{i-1} f_j (i-j) \]

列舉最後一個數為 \(i-j\) 的思路。

但是你會發現一個問題。因為 \(f_i\) 中不包含 \(i=i\) 的拆分;但轉移中需要這個拆分。

於是換一換,令 \(f_i\) 表示 所有將 \(n\) 分解為至少 \(1\) 個正整數之和的乘積之和,上面的轉移就是對的。

考慮 \(f_i\) 和很多東西有關,無法用矩陣優化。

我們試圖讓它只和 \(f_{i-a}\)\(a\) 為常數)有關。

考慮:

\[f_i - f_{i-1} = \sum_{j=0}^{i-1} f_j(i-j) - \sum_{j=0}^{i-2} f_j(i - 1 - j) \]

\[= f_{i-1} + \sum_{j=0}^{i-2} f_j \]

\[= \sum_{j=0}^{i-1} f_j \]

這不就是字首和麼。用 \(g\) 表示 \(f\) 的字首和。易得:

\[\begin{cases} f_i = f_{i-1} + g_{i-1} \\ g_i = g_{i-1} + f_i = f_{i-1} + 2 * g_{i-1}\\ \end{cases} \]

考慮如何用矩陣維護它。易得:

\[\begin{vmatrix}1&1\\1&2\end{vmatrix} \times \begin{vmatrix} f_{i-1} \\ g_{i-1} \end{vmatrix} = \begin{vmatrix} f_i \\ g_i \end{vmatrix} \]

然後直接維護就完了。

另外需要注意的是邊界,坑了我好久。

\(f_0=1 , g_0 = 0\),和定義不同,注意一下。

另外,答案為 \(f_n - n\). \(-\) 的時候可能會成負數,要處理一下,這個又坑了我好久。

時間複雜度:\(\mathcal{O}(T \log n)\).

#include<bits/stdc++.h>
using namespace std;

#define int long long
const int MOD=1e9+7;

inline int read(){char ch=getchar(); int f=1; while(ch<'0' || ch>'9') {if(ch=='-') f=-f; ch=getchar();}
	int x=0; while(ch>='0' && ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar(); return x*f;}

inline void write(int x) {
	if(x<0) {putchar('-');write(-x);return;}
	if(x<10) {putchar(char(x%10+'0'));return;}
	write(x/10);putchar(char(x%10+'0'));
}

struct martix {
	int a[3][3];
	
	inline void print(martix a) {
		for(int i=1;i<=2;i++) {
			for(int j=1;j<=2;j++) printf("%d ",a.a[i][j]);
			puts("");
		} puts("");
	}
	
	inline martix chengfa(martix a,martix b) {
		martix ans; memset(ans.a,0ll,sizeof(ans.a));
		for(int i=1;i<=2;i++)
		for(int j=1;j<=2;j++)
		for(int k=1;k<=2;k++) 
			ans.a[i][j]=(ans.a[i][j]+a.a[i][k]*b.a[k][j]%MOD)%MOD;
		return ans;
	}
	
	inline martix pw(martix a,int x) {
		martix ans; memset(ans.a,0ll,sizeof(ans.a));
		for(int i=1;i<=2;i++) 
		for(int j=1;j<=2;j++) ans.a[i][j]=a.a[i][j];
		x--;
		while(x) {
//			a.print(a); ans.print(ans);
			if(x&1) ans=chengfa(ans,a);
			a=chengfa(a,a); x>>=1;
		} /*a.print(a); ans.print(ans);*/ return ans;
	}
	
} ;

signed main() {
	int T=read(),n; while(T--) {
		n=read();
		martix p;
		p.a[1][1]=1ll; p.a[1][2]=1ll; p.a[2][1]=1ll; p.a[2][2]=2ll;
		martix ans=p.pw(p,n);
		printf("%lld\n",(ans.a[1][2]-n+MOD)%MOD);
	}
	return 0;
}