21738 牛牛与数组
#include <iostream>
using namespace std;
/*1:长度为n
**2:每一个数都在1到k之间
**3:对于任意连续的两个数A,B,A<=B 与(A % B != 0) 两个条件至少成立一个
**请问一共有多少满足条件的数组,对1e9+7取模
**1 ≤ n ≤ 10 1 ≤ k ≤ 100000*/
/**任意连续指的是啥 “A B连续” - A B?**/
typedef long long ll;
const int mod = 1e9 + 7;
int dp[11][100001];//用dp[i][j]记录结果,表示在第i位放j的方案数
int main()
{
int n = 0, k = 0;
ll ans = 0, sum = 0, sum1 = 0;
cin >> n >> k;
//按位处理 先处理1位
for(int i = 0;i <= k;i++)
{
dp[1][i] = 1; //均只有1种情况
}
//处理2位+
for(int i = 2;i <= n;i++) //1位已经过处理 从第二位向后
{
sum = 0;
//符合条件太难找 最好找不符合条件的
//全部可能情况
for(int j = 1;j <= k;j++)
{
sum += dp[i - 1][j]; //全部情况 也就是全都选 上一位多少方案数就是多少
/**correct 记得随时取余**/
sum %= mod;
}
//不符合条件的情况
for(int j = 1;j <= k;j++)
{
sum1 = 0;
//A<=B 与(A % B != 0)
//要么小于 要么不是倍数
//所以不符合的条件即为: 大于且是倍数
for(int z = j + j;z <= k;z += j)
{
sum1 += dp[i - 1][z];
sum1 %= mod;
}
/**初 不知道在哪里统计答案**/
dp[i][j] = (sum - sum1) % mod;
}
}
/**初 如何统计最终答案 不是写二重循环!**/
for(int j = 1;j <= k;j++)
{
ans += dp[n][j];
ans %= mod; //不要忘记取余z
}
cout << ans << endl;
return 0;
}
dp的核心是要找到状态转移方程,也就是要把问题归纳为相似步骤的不断重复(在这个过程中产生种种状态)。在具体解决的时候,就是找到当前状态与上一状态(或下一状态)的关系(表达式)。
本问题中可以把找数组种类的过程看成在手动生成数组,是从第一位到第n位逐一选择,完成全过程后便得到了可能的数组。dp[i][j]的表达可能不太好想,我看了题解才想到。用i来标记第i位,用j表示当前位选择j的所有可能方案数。
状态转移的过程其实就是在i-1的基础上,挑选可行的j(从1-k题给范围选)放在第i位上。因为条件是或,会比较复杂,如果我们从反面考虑会简单一些。那么当前方案数就是 全部方案 - 不可行方案,这里要注意全部方案其实就是所有的j都选入,也就是dp[i - 1][j]!**还有一点待解决的问题就是为什么从0开始计数