官方题解 | 计数改良
2025-01-05 22:44:00
发布于:云南
20阅读
0回复
0点赞
20pts
深搜,参考下面的代码.
#include <iostream>
#include <cstdio>
using namespace std;
int n;
int dfs(int ct, bool a, bool b, bool c){//a代表有没有3,b代表有没有5,c代表有没有7
if(ct > n) return (a && b && c);
return dfs(ct + 1, 1, b, c) + dfs(ct + 1, a, 1, c) + dfs(ct + 1, a, b, 1);
}
int main(){
cin.tie(nullptr) -> sync_with_stdio(0);
cout.tie(nullptr) -> sync_with_stdio(0);
int t;
cin >> t;
while(t--){
cin >> n;
cout << dfs(1, 0, 0, 0) << '\n';
}
return 0;
}
对于每次查询,时间复杂度:.
40pts
我们发现前四个测试点答案也就不超过 种,考虑记忆化.
#include <iostream>
#include <cstdio>
#define int long long
using namespace std;
int n;
const int mod = 1e9 + 7;
int ans[1005];
int dfs(int ct, bool a, bool b, bool c){//a代表有没有3,b代表有没有5,c代表有没有7
if(ct > n) return (a && b && c);
return (dfs(ct + 1, 1, b, c) + dfs(ct + 1, a, 1, c) + dfs(ct + 1, a, b, 1)) % mod;
}
signed main(){
cin.tie(nullptr) -> sync_with_stdio(0);
cout.tie(nullptr) -> sync_with_stdio(0);
int t;
cin >> t;
while(t--){
cin >> n;
if(ans[n]) cout << ans[n] << '\n';//记忆化
else cout << (ans[n] = dfs(1, 0, 0, 0)) << '\n';
}
return 0;
}
对于每次查询,时间复杂度: 或 .
总时间复杂度:.
100pts
既然最终答案能记忆化,那能不能每个过程都记忆化一遍呢?
我们可以记录第 位每种 出现情况的种类数,分类讨论.
将 都出现的情况记作 ,将 出现两个的情况记作 ,将 出现一个的情况记作 .
:
- 可以由上一个 填上 获得,情况数 .
- 可以由上一个 填上所缺的数获得.
:
- 可以由上一个 填上 中的两个数字获得,情况数 .
- 可以由上一个 填上所缺的数获得.
:
可以由一个都没有获得.
#include <iostream>
#include <cstdio>
using namespace std;
const int mod = 1e9 + 7;
int dp[10000005][8];
signed main(){
cin.tie(nullptr) -> sync_with_stdio(0);
cout.tie(nullptr) -> sync_with_stdio(0);
dp[0][0] = 1;
for(int i = 1; i <= 10000000; i++){
for(int j = 1; j <= 7; j++){
dp[i][j] = 1ll * __builtin_popcount(j) * dp[i - 1][j] % mod;//由上一个相同的
if(j & 1) dp[i][j] = (dp[i][j] + dp[i - 1][j ^ 1]) % mod;//无 -> C
if(j & 2) dp[i][j] = (dp[i][j] + dp[i - 1][j ^ 2]) % mod;//C -> B
if(j & 4) dp[i][j] = (dp[i][j] + dp[i - 1][j ^ 4]) % mod;//B -> A
}
}
int t;
cin >> t;
while(t--){
int n;
cin >> n;
cout << dp[n][7] << endl;
}
return 0;
}
对于每次查询,时间复杂度:.
预处理时间复杂度:.
总时间复杂度:.
如果你嫌内存占用太大了,你可以多花费 的时间复杂度转离线,把空间复杂度降至 .
120pts
Macw老师提出来一个快速的办法,理论上可以通过 的数据!
首先是随便选取 中的任意一位,总共有 种情况.
然后减去只选择两个数的,即只选 的,只选 的和只选 的,共有 种情况.
我们发现只选一个数的情况多选了一次,所以需要加回,即将结果 .
这样就可以以 的时间复杂度求出答案了!
我们注意到 与 均互质,所以对于更大的数可以用费马小定理来将 降至 以内.
#include <iostream>
#include <cstdio>
#define int long long
using namespace std;
const int mod = 1e9 + 7;
int pow(int n, int k){//快速幂
int ans = 1;
while(k){
if(k & 1) ans = ans * n % mod;
n = n * n % mod, k >>= 1;
}
return ans;
}
signed main(){
int t;
cin >> t;
while(t--){
int n;
cin >> n;
if(n < 3) cout << "0\n";//特判不足3的
else cout << ((pow(3, n) - pow(2, n) * 3 % mod + 3) % mod + mod) % mod << '\n';//计算结果
}
return 0;
}
对于每次查询,时间复杂度:.
这里空空如也
有帮助,赞一个