2019 上海网络赛 D. Counting Sequences I (dfs + 剪枝)

本文介绍了一种使用暴力算法解决特定数学序列问题的方法。通过分析发现,非1的数字个数有限,最大数字不超过n。文章详细解释了为什么存在大于n的数时,序列中的最小数也将大于n,从而证明了暴力打表的有效性。文中还提供了具体的C++代码实现,包括递归枚举非1数字、剪枝策略和快速幂运算等关键部分。

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

在这里插入图片描述


(你以为是个结论题或者规律题,结果他是个暴力题)

通过小范围数据打表会发现非1的数字个数非常有限,且最大数字不会超过 n。

证明:假设存在一个数大于 n,让它尽量小,设为 n + 1,因为至少存在两个大于 1的数字,让令一个大于 1 的数字也尽量小,设为 2,可以计算出对应项:2 * (n + 1) - (n + 1 + 2) + 2 = n + 1,也就是说当存在一个数大于 n 时,最小的一项也会大于 n。

那么直接暴力打表就完事了。按递增的顺序枚举非 1 的数字,可以计算出对应项,当项数超过3000时退出递归。按顺序枚举还有一个提前退出的剪枝: a ∗ b ∗ c ∗ ( d + 1 ) a * b * c * (d + 1) abc(d+1) 对应的项数 > > > a ∗ b ∗ c ∗ d a * b * c *d abcd,当 a ∗ b ∗ c ∗ d a * b * c * d abcd 对应项超出 3000 时,后面的就不需要枚举了,也不需要继续递归下去,可以直接回到上一层递归。

实际这样跑非常非常快,3ms就打完了3000个数字。


代码:

#include<bits/stdc++.h>
using namespace std;
const int mod = 1e9 + 7;
typedef long long ll;
const int maxn = 3e3 + 100;
ll dp[maxn];
ll fact[maxn],ifact[maxn];
int g[maxn],top = 0;
int t,n;
ll fpow(ll a,ll b) {
	ll r = 1;
	while(b) {
		if(b & 1) r = r * a % mod;
		a = a * a % mod;
		b >>= 1;
	}
	return r;
}
bool dfs(int cur,int lst) {
	if(cur > 2) {
		ll v = -1,num = 0,res = 1,sum = 0;
		ll ans = 1;
		for(int i = 1; i <= top; i++) {
			int it = g[i];
			res = res * it;
			sum = sum + it;
			if(it != v) {
				ans = ans * ifact[num] % mod;
				num = 1;
				v = it;
			}
			else {
				num++;
			}
		}	
		ans = ans * ifact[num] % mod;
		int p = res - sum + cur - 1;
		if(p > 3000) return false;
			dp[p] = (dp[p] + ifact[res - sum] * fact[p] % mod * ans % mod) % mod;
		if(p == 3000) return false;
	}
	for(int i = lst; i <= 3000; i++) {
		g[++top] = i;
		bool f = dfs(cur + 1,i);
		top--;
		if(!f) break;
	}
	return true;
}
int main() {
	fact[0] = 1;
	for(int i = 1; i <= 3000; i++) {
		fact[i] = fact[i - 1] * i % mod;	
	}
	ifact[3000] = fpow(fact[3000],mod - 2);
	for(int i = 2999; i >= 0; i--)
		ifact[i] = ifact[i + 1] * (i + 1) % mod;
	dfs(1,2);
	scanf("%d",&t);
	while(t--) {
		scanf("%d",&n);
		printf("%lld\n",dp[n]);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值