hdu多校(一)1005 Fibonacci Sum(组合数学、fibo通项公式,二次剩余,数学)

1005 Fibonacci Sum

题意:
给定N、C( 1 0 18 10^{18} 1018)、K ( 1 0 5 ) (10^5) (105)对于Fibonacci数列进行 F 0 K + F C K + F 2 C K + . . . + F N C K F_0^{K}+F_C^{K}+F_{2C}^{K}+...+F_{NC}^{K} F0K+FCK+F2CK+...+FNCK进行mod ( 1 0 9 + 9 ) (10^9+9) (109+9)的计算
思路:
已知Fibonacci数列有个通项公式: F n = 1 5 [ ( 1 + 5 2 ) n − ( 1 − 5 2 ) n ] F_n=\frac{1}{\sqrt5}[(\frac{1+\sqrt5}{2})^n-(\frac{1-\sqrt5}{2})^n] Fn=5 1[(21+5 )n(215 )n]
s q 5 = 1 5 , a = 1 + 5 2 , b = 1 − 5 2 sq5=\frac{1}{\sqrt5},a=\frac{1+\sqrt5}{2},b=\frac{1-\sqrt5}{2} sq5=5 1,a=21+5 ,b=215 (程序计算得到 s q 5 = 383008016 sq5=383008016 sq5=383008016

  • 代入通项公式 ⇒ s q 5 K ∗ ∑ i = 0 N ( a i C − b i c ) K \Rightarrow sq5^K*\sum_{i=0}^N (a^{iC}-b^{ic})^K sq5Ki=0N(aiCbic)K
  • 二项式展开 ⇒ \Rightarrow s q 5 K ∗ ∑ i = 0 N ∑ j = 0 K C k j a i j C ( − b ) ( k − j ) ∗ i C sq5^K*\sum_{i=0}^N \sum_{j=0}^K C_{k}^{j}a^{ijC}(-b)^{(k-j)*iC} sq5Ki=0Nj=0KCkjaijC(b)(kj)iC

A = a C , B = b C A=a^C,B=b^C A=aC,B=bC

  • 代入 ⇒ s q 5 K ∗ ∑ i = 0 N ∑ j = 0 K C k j A i j ( − B i ) k − j \Rightarrow sq5^K*\sum_{i=0}^N \sum_{j=0}^K C_{k}^{j}A^{ij}(-B^i)^{k-j} sq5Ki=0Nj=0KCkjAij(Bi)kj
  • 交换 ⇒ s q 5 K ∗ ∑ j = 0 K C k j ( − 1 ) k − j ∑ i = 0 N ( A j ) i ( B k − j ) i \Rightarrow sq5^K* \sum_{j=0}^KC_{k}^{j}(-1)^{k-j}\sum_{i=0}^N (A^j)^i(B^{k-j})^i sq5Kj=0KCkj(1)kji=0N(Aj)i(Bkj)i
  • ∑ i = 0 N ( A j ) i ( B k − j ) i ⇒ ∑ i = 0 N ( A j ∗ B k − j ) i \sum_{i=0}^N (A^j)^i(B^{k-j})^i\Rightarrow\sum_{i=0}^N (A^j*B^{k-j})^i i=0N(Aj)i(Bkj)ii=0N(AjBkj)i

有等比求和公式, ∑ i = 0 N x i = x N + 1 − 1 x − 1 \sum_{i=0}^N x^i=\frac{x^{N+1}-1}{x-1} i=0Nxi=x1xN+11

  • ∑ i = 0 N ( A j ∗ B k − j ) i ⇒ ( A j B k − j ) N + 1 − 1 A j B k − j − 1 \sum_{i=0}^N (A^j*B^{k-j})^i\Rightarrow \frac{(A^jB^{k-j})^{N+1}-1}{A^jB^{k-j}-1} i=0N(AjBkj)iAjBkj1(AjBkj)N+11

所以问题就转化成了: s q 5 K ∗ ∑ j = 0 K C k j ( − 1 ) k − j ( A j B k − j ) N + 1 − 1 A j B k − j − 1 sq5^K*\sum_{j=0}^KC_{k}^{j}(-1)^{k-j} \frac{(A^jB^{k-j})^{N+1}-1}{A^jB^{k-j}-1} sq5Kj=0KCkj(1)kjAjBkj1(AjBkj)N+11 ( A = a C , B = b C ) (A=a^C,B=b^C) (A=aC,B=bC)
这个代码就比较好写啦^^
注意点:
直接在for循环里面qpow会t,代码如下:

LL fac[maxn], invfac[maxn];
LL sqt5 = 383008016;//根号5二次剩余
LL inv2, invsq5;
LL a, b;
void initfac(LL n) { 
	fac[0] = 1; 
	for (LL i = 1; i <= n; i++) { fac[i] = fac[i - 1] * i%MOD; }
	invfac[n] = quick_mod(fac[n], MOD - 2, MOD);
	for (LL i = n - 1; i >= 0; i--) { invfac[i] = invfac[i + 1] * (i + 1) % MOD; }
	return; 
}
LL cm(LL n, LL m) {
	if (m<0 || m>n) return 0;
	return fac[n] * invfac[n - m] % MOD*invfac[m] % MOD;
	//return (fac[n] * qpow(fac[m], MOD - 2) % MOD*qpow(fac[n - m], MOD - 2) % MOD) % MOD;
}

LL solve(LL n, LL c, LL k) {
	LL ans = 0;
	LL A, B;
	LL tt, fenmu, CK;
	A = quick_mod(a, c % (MOD - 1), MOD);
	B = quick_mod(b, c % (MOD - 1), MOD);
	/*cout << "ans1: " << -1 * cm(1, 0)*(quick_mod(B, (n + 1) % (MOD - 1), MOD) - 1)*(quick_mod(B - 1, MOD - 2, MOD)) % MOD << endl;
	cout << "ans2:" << cm(1, 1)*(quick_mod(A, (n + 1) % (MOD - 1), MOD) - 1)*(quick_mod(A - 1, MOD - 2, MOD)) % MOD << endl;*/

	for (LL j = 0; j <= k; j++) {
		tt = (quick_mod(A, j, MOD)*quick_mod(B, k - j, MOD)) % MOD;
		if (tt == 1) {
			tt = cm(k, j)*(n + 1) % MOD;
			if ((k - j) % 2)tt *= -1;
			ans = (tt + ans + MOD) % MOD;
			continue;
		}
		fenmu = (tt - 1 + MOD) % MOD;
		tt = quick_mod(tt, (n + 1) % (MOD - 1), MOD);
		tt = (tt - 1 + MOD) % MOD;
		CK = tt * quick_mod(fenmu, MOD - 2, MOD) % MOD;
		CK = (cm(k, j)*CK) % MOD;
		//cout << cm(k, j) << endl;
		if ((k - j) % 2)CK *= -1;
		//cout << "teat: " << CK << endl;
		//cout << "test2: " << CK << endl;
		ans = (CK + ans + MOD) % MOD;
	}
	ans = (quick_mod(invsq5, k, MOD)*ans) % MOD;//sq5^K
	return ans;
}
int main() {
	int t;
	LL n, c, k;
	inv2 = quick_mod(2, MOD - 2, MOD);//2的逆元
	invsq5 = quick_mod(sqt5, MOD - 2, MOD);//根号5分之一的逆元
	a = (1 + sqt5)*inv2%MOD;
	b = (1 - sqt5 + MOD) % MOD*inv2%MOD;
	//cout << a << " " << b << endl;
	sci(t);
	/*for (LL i = 1; i <= MOD; i++) {
		if (i*i%MOD == 5) { cout << i << endl; break; }
	}*/
	initfac(1e5+5);//初始化,之后要计算排列组合
	while (t--)
	{
		scanf("%lld%lld%lld", &n, &c, &k);
		printf("%lld\n", solve(n, c, k));
	}
	return 0;
}

优化:
在j的循环内对于 ( A j B k − j ) N + 1 (A^jB^{k-j})^{N+1} (AjBkj)N+1的循环进行优化,不要每次都qpow,因为他们有一个初始值 A 0 B k A^0B^k A0Bk,然后只需要每次进行增量 A B − 1 AB^{-1} AB1,就可以得到这次的 A j B k − j A^jB^{k-j} AjBkj
cndmmdldasfhilskkju气死我了wa死了找不出错哪了不管了谁爱做谁做cnmmmmm
🐂我找到了^^tt=1的时候没有mod,好,不愧是我

LL fac[maxn], finv[maxn];
LL sqt5 = 383008016ll;//根号5二次剩余
LL inv2, invsq5;
LL a, b;
void initfac(LL n) {
	fac[0] = 1; for (LL i = 1; i <= n; i++) { fac[i] = fac[i - 1] * i%MOD; }
	finv[n] = quick_mod(fac[n], MOD - 2);
	for (LL i = n - 1; i >= 0; i--) { finv[i] = finv[i + 1] * (i + 1ll) % MOD; }
	return;
}
LL cm(LL n, LL m) {//计算组合数
	if (m<0 || m>n) return 0;
	return (fac[n] * finv[n - m]) % MOD*finv[m] % MOD;
	//return (fac[n] * qpow(fac[m], MOD - 2) % MOD*qpow(fac[n - m], MOD - 2) % MOD) % MOD;
}

LL solve(LL n, LL c, LL k) {
	LL ans = 0ll;
	LL A, B;
	LL tt, chushi;
	A = quick_mod(a, c % (MOD - 1ll));
	B = quick_mod(b, c % (MOD - 1ll));
	LL del = A * quick_mod(B, MOD - 2ll) % MOD;
	chushi = quick_mod(B, k );//初始值:B^k
	/*cout << "ans1: " << -1 * cm(1, 0)*(quick_mod(B, (n + 1) % (MOD - 1), MOD) - 1)*(quick_mod(B - 1, MOD - 2, MOD)) % MOD << endl;
	cout << "ans2:" << cm(1, 1)*(quick_mod(A, (n + 1) % (MOD - 1), MOD) - 1)*(quick_mod(A - 1, MOD - 2, MOD)) % MOD << endl;*/

	for (LL j = 0; j <= k; j++) {
		if (chushi == 1) {
			tt = (n + 1ll) % MOD;
		}
		else {
			tt = (((quick_mod(chushi, (n + 1ll) % (MOD - 1)) - 1 + MOD) % MOD)*(quick_mod((chushi - 1 + MOD) % MOD, MOD - 2))) % MOD;
		}
		tt = cm(k, j)*tt%MOD;
		if ((k - j) % 2 ) {
			ans = (ans - tt + MOD) % MOD;
		}
		else {
			ans = (ans + tt) % MOD;
		}
		chushi = (chushi * del) % MOD;
	}
	ans = (quick_mod(invsq5, k)*ans) % MOD;//sq5^K
	return ans;
}
int main() {
	int T;
	LL n, c, k;
	inv2 = quick_mod(2ll, MOD - 2ll);//2的逆元
	invsq5 = quick_mod(sqt5, MOD - 2ll);//根号5分之一的逆元
	//cout << invsq5 << endl;
	a = (1ll + sqt5)*inv2%MOD;
	b = (1ll - sqt5 + MOD) % MOD*inv2%MOD;
	//cout << a << " " << b << endl;
	sci(T);
	initfac(1ll*1e5);//初始化,之后要计算排列组合
	while (T--)
	{
		scanf("%lld%lld%lld", &n, &c, &k);
		printf("%lld\n", solve(n, c, k));

	}
	return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值