BZOJ3516 国王奇遇记 [推式子/DP]

本文探讨了一种处理大规模数值的算法,通过动态规划解决与m相关的复杂问题。介绍了如何利用m的答案推导出m+1的答案,实现O(m^2)的时间复杂度。涉及大数运算、动态规划及组合数学的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门

ans=\sum_{i=1}^ni^m*m^i  ,  n <= 1e9, m <= 1e3

由于 n 太大,无法枚举,我们需要一种与 m 有关的算法,比较容易想到由 m 的答案 推到 m + 1 的答案

f(n,j)=\sum_{i=1}^n i^j*m^i=m*\sum_{i=0}^{n-1} ( i+1)^j*m^i=m*\sum_{i=0}^{n-1}\sum_{k=0}^j \binom{j}{k}i^k*m^i=m*\sum_{k=0}^j \binom{j}{k} \sum_{i=0}^{n-1}i^k*m^i=m*\sum_{k=0} ^ j \binom{j}{k } f(n-1,k)=m*\sum_{k=0} ^ j \binom{j}{k } (f(n,k)-n^k*m ^n )

然后就可以 O(m^2) DP 了


#include<bits/stdc++.h>
#define N 1050
using namespace std;
const int Mod = 1000000007;
typedef long long ll;
ll add(ll a, ll b){ return (a + b) % Mod;}
ll mul(ll a, ll b){ return a * b % Mod;}
ll power(ll a, ll b){ ll ans = 1;
	for(;b;b>>=1){if(b&1) ans = mul(ans, a); a = mul(a, a);}
	return ans;
}
ll n, m, c[N][N], f[N], pw[N];
int main(){
	scanf("%lld%lld", &n, &m);
	if(m == 1){ printf("%lld", (n * (n + 1) / 2) % Mod); return 0;}
	c[0][0] = 1;
	for(int i = 1; i <= m; i++){
		c[i][0] = 1; for(int j = 1; j <= i; j++) c[i][j] = add(c[i-1][j-1], c[i-1][j]);
	}
	f[0] = mul(add(power(m, n + 1), Mod - 1), power(m - 1, Mod - 2));
	ll A = power(m, n);
	pw[0] = 1; for(int i = 1; i <= m; i++) pw[i] = mul(pw[i-1], n);
	ll inv = power(Mod + 1 - m, Mod - 2);
	for(int i = 1; i <= m; i++){
		ll sum = 0;
		for(int j = 0; j < i; j++){
			sum = add(sum, mul(c[i][j], add(f[j], Mod - mul(pw[j], A))));
		} sum = add(sum, Mod - mul(pw[i], A));
		sum = mul(sum, mul(m, inv)); f[i] = sum;
	} cout << f[m]; return 0;
} 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FSYo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值