|
马上注册,结交更多好友,享用更多功能^_^
您需要 登录 才可以下载或查看,没有账号?立即注册
x
问题描述
构造长度为 n ( < 10) 的序列, 值域 [1, k] (k < 1e5)
使得前后相邻的两个数 A, B 满足条件 (A <= B || A % B != 0)
求方案数
错误原因
优化问题
80pts 写出来了, 没想到优化的方法 , 没注意到如果只循环倍数是 O(logn) 的
忘记取模了
解题思路
设 f(i, j) 是填完前 i 个数, 最后结尾的数字是 j 的方案数
转移: f(i, j) += f(i - 1, k) ( k 满足题目中的条件即可)
改进
只和 i - 1 有关, 压成 1 维, f(j)
用 sum 记录 i - 1 的 所有 f(j) 之和, 下一轮转移减去倍数
发现一开始转移的复杂度过高, 正难则反, 考虑减掉 j 的倍数, 可以控制在 O(nklogk)
总结
要做的事情太多, 可以考虑反面情况, 用总数减去不合法的情况可以减少复杂度
- #include <bits/stdc++.h>
- using namespace std;
- typedef long long ll;
- const ll P = 1e9 + 7;
- const int N = 1e5 + 5;
- int n, k;
- ll f[N], sum;
- int main(){
- ios::sync_with_stdio(0);
- cin.tie(0);
- cin >> n >> k;
- sum = k;
- for(int i = 1; i <= k; i++) f[i] = 1;
- for(int i = 2; i <= n; i++){ // 第几个数
- for(int j = 1; j <= k; j++){ // 末尾的数值
- f[j] = sum;
- for(int s = j*2; s <= k; s += j) f[j] = (P + (f[j] - f[s]) % P) % P;
- }
- sum = 0;
- for(int j = 1; j <= k; j++) sum = (sum + f[j]) % P;
- }
- cout << sum;
- return 0;
- }
复制代码
|
|