第一行输入整数
——数组长度。
第二行输入
个整数
——初始数组。
输出一行
个整数,第
个数表示最终结果为
的方案数(按
)。
4 1 2 3 4
1 0 0 0 3 3 0 0 0 1
喵呜~主人不会这道题喵?其实只需要会dp就可以做出来了喵!
我们可以“从后往前思考”喵!
这里dp数组就像猫猫的“记忆小本本”,记录着当前状态下个位数为0-9的方案数。开始时只考虑最后一个数字,所以它的个位数位置记为1喵~
对于每个新数字,猫猫会检查所有可能的个位数组合,并用新的小本本记录喵!
每次处理完一个数字,就用新的“记忆小本本”替换旧的,继续向前探索喵~
假设数字是 [2, 3, 4]:
1.开始:只考虑4 → dp[4] = 1
2.加入3:与4组合产生7(加)和2(乘) → dp[7] = 1, dp[2] = 1
3.加入2:与7组合产生9(加)和4(乘),与2组合产生4(加)和4(乘)
4.最终:dp[4] = 3, dp[9] = 1(其他都是0喵)
这种方法避免了重复计算喵每次只关心个位数,大大简化了问题喵~
下面可以看代码喵!
#include <bits/stdc++.h>
using namespace std;
using ll=long long;
const ll mod=1e9+7;
int main() {
cin.tie(0)->sync_with_stdio(0);
int n;cin >> n;
vector<ll> a(n);
for(int i=0;i<n;i++)
{
cin >> a[i];
}
vector<ll> dp(10,0);
if(n==1)//只有一个元素时
{
if(a[0]<10) dp[a[0]]=1;//如果该元素小于10,就有范围内方案
for(int i=0;i<10;i++) cout << dp[i] << ' ';//直接输出
return 0;
}
dp[a[n-1]%10]=1;//初始化:将最后一位加入dp中
for(int i=n-2;i>=0;i--)
{
vector<ll> xin(10,0);//新形态数组
int now=a[i]%10;//当前数的个位数
for(int j=0;j<10;j++)
{
//加法操作:(now+j)%10
int jia=(now+j)%10;
xin[jia]+=dp[j];
xin[jia]%=mod;
//乘法操作:(now*j)%10
int cheng=(now*j)%10;
xin[cheng]+=dp[j];
xin[cheng]%=mod;
}
dp=xin;
}
for(int i=0;i<10;i++) cout << dp[i] << ' ';
}
/*
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣤⣤⡀⣀⣠⣤⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣀⡀⢀⣴⣾⣷⣶⣾⣿⣿⣿⣿⣿⣿⣿⣿⣷⣾⣿⣷⣦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣠⣾⣿⣿⣿⣿⣿⣿⣿⠿⠛⠛⠉⠉⠉⠉⠉⠉⠛⠻⠿⣿⣿⣿⣿⣿⣶⣤⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⠀⢠⣾⣿⣿⣿⡿⠿⠛⠉⠉⠉⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠙⠿⣿⣿⣿⣷⣄⡀⠀⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⠀⠀⣀⣿⣿⣿⠟⠁⠀⠀⠀⠀⠀⠀⠀⣰⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⠿⣿⣿⣿⡄⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⣠⣾⣿⣿⠟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣶⣄⠀⠀⠀⠀⠀⢀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⣻⣿⣿⠀⠀⠀⠀⠀⠀
⠀⠀⠀⠀⠀⢹⣿⡿⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⣿⠁⠈⢢⡀⠀⠀⠀⢸⡇⢀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠻⣿⡟⠒⢦⡀⠀⠀⠀
⠀⠀⣠⣤⣤⣼⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠸⡇⠀⠀⠀⠉⢢⣄⠀⠀⢿⠊⠳⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⣷⡄⠀⢷⠀⠀⠀
⠀⢰⠇⠀⣰⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⠀⠀⡌⣹⠗⠦⣬⣇⠀⠉⢢⡀⠀⠀⠀⠀⠀⠀⠀⠀⠘⣿⡀⢸⡄⠀⠀
⠀⡟⠀⣼⣯⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢰⣆⢹⡀⠀⠀⠀⠉⠁⠀⠀⢀⣀⡁⠀⠀⠉⠳⢴⡆⠀⠀⠀⠀⠀⠀⢹⣧⠈⡇⠀⠀
⠀⡇⠀⠀⢻⣦⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣾⠻⠉⠛⠂⠀⠀⠀⠀⠀⠀⠻⠿⣿⣿⣿⣶⣦⡀⠛⣇⠀⠀⠀⠀⠀⣈⣿⠀⡇⠀⠀
⢸⡇⠀⠀⢠⣿⣷⣦⣀⡸⣷⣦⣶⡂⠉⠉⠉⢁⣤⣶⡶⠀⠀⠀⣀⣀⡴⠀⠀⠀⠀⠀⠀⠈⠉⠉⠁⠀⡟⢀⣴⣟⣰⣾⣿⣏⣠⠇⠀⠀
⠈⡇⠀⠀⢸⣿⠁⠉⣿⠛⠛⠃⡇⠀⠀⢠⣶⣿⡿⠛⠁⠀⠀⠉⠁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠼⢿⠟⠿⢿⡏⠀⠘⣿⡀⠀⠀⠀
⠀⢷⣀⣀⣿⠇⠀⠀⢿⡇⠀⢀⢱⡀⠀⠛⠛⠉⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣼⠀⠀⢸⠇⠀⠀⢹⣿⣄⠀⠀
⠀⠀⣉⣿⡏⠀⠀⠀⠀⠀⠀⢸⣇⣳⡄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡰⣿⠃⠀⠀⠀⠀⠀⠀⣿⠈⢧⠀
⠀⠘⣿⣿⠁⠀⠀⠀⠀⠀⠀⠘⣿⡛⣶⠀⠀⣠⠔⠒⠛⠒⠦⡀⠀⠀⠀⠀⣠⡤⠶⠤⢤⣀⠀⠀⠀⢀⣏⡄⠀⠀⠀⠀⠀⡀⣿⡆⠈⣧
⣠⡾⠛⣿⣿⣧⠀⠀⠀⠀⢸⣿⠾⢿⡿⠀⣰⠃⠀⠀⠀⠀⠀⢹⡄⠀⠀⡼⠁⠀⠀⠀⠀⠈⠙⣦⠀⢸⣿⡇⣾⣣⡀⠀⢰⣿⣿⣿⣤⠾
⡟⠀⠀⠻⣿⡟⢷⡄⣤⡀⠈⣿⡀⣸⠇⠀⠏⠀⠀⠀⠀⠀⠀⠀⡇⠀⠀⡇⢀⡀⠀⠀⠀⠀⢀⡟⠀⠀⠋⣿⣿⣿⡇⣠⣿⠿⠛⢷⡀⠀
⠀⠀⠀⠀⣿⣇⣨⣿⣿⣿⣦⣽⣷⡟⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠁⠀⠀⠃⠀⠙⠢⠤⠤⠴⢾⠀⠀⠀⠀⢸⣷⣿⣿⠟⠁⠀⠀⠈⣧⠀
⠀⠀⠀⠀⠈⠉⠉⠁⠈⠉⠈⢉⣿⡁⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠀⠀⠀⠀⢸⡇⠀⠀⠀⠀⠀⠀⠀⣿⠀
*/
噗嗤——这种一眼就能看出是倒排动态规划(DP)的题目,居然还有杂鱼在纠结?逻辑简单到连路边的牛都能秒懂吧?
既然你们这些大脑空空的家伙还在搜索题解,本天才少女翔子就勉为其难地给你们讲讲。好好听着,这可是杂鱼们唯一的救赎机会哦!详情可看**************************
题面说每次操作“最后两个数 ”,这意味着什么?这意味着这个数组的合并顺序是从右往左固定的!
想象一下,你有一排数字 。
这不就是个简单的后缀处理吗?如果你连这个合并顺序都看不出来,建议直接把杂鱼❤键盘捐给有需要的人,呵呵❤~
我们只需要记录:处理到第 个数字时,后面所有数字合并出的结果为 的方案数分别有多少。
dp[i][j] 表示从第 个数到第 个数,合并后的结果为 的方案数。 new_val = (a[i] + j) % 10 new_val = (a[i] * j) % 10 dp[i][new_val] += dp[i+1][j] 这种 的复杂度,简直是闭着眼都能跑过,只有没用的❤杂鱼❤才会担心超时吧❤?
这里的代码宏定义写得这么多,真是杂鱼最后的倔强呢❤~
#include <bits/stdc++.h>
#define il inline
#define double long double
using namespace std;
using ll = long long;
using ull = unsigned long long;
using int128 = __int128_t;
const ll N = 5e5 + 5, mod = 1e9+7, inf = 2e18;
const double eps = 1e-9;
double PI = 3.1415926;
il void solve(){
int n;
cin>>n;
if(n==1){
ll a;
cin>>a;
for(int i=0;i<10;i++){
cout<<(i==a%10?1:0)<<' ';
}
return ;
}
vector<ll>a(n+1);
for(int i=1;i<=n;i++){
cin>>a[i];
a[i]%=10; // 杂鱼记得取模,不然爆掉就搞笑了
}
// 这个DP表大得离谱,不过谁让内存不值钱呢
vector<vector<ll>>dp(n+5,vector<ll>(10));
dp[n][a[n]]=1;
for(int i=n-1;i>=1;i--){
for(int j=0;j<10;j++){
// 乘法合并,智商乘法也是0吗?
ll ed=j*a[i]%10;
(dp[i][ed]+=dp[i+1][j])%=mod;
// 加法合并,像不像在数自己的失误次数?
ed=(j+a[i])%10;
(dp[i][ed]+=dp[i+1][j])%=mod;
}
}
for(int i=0;i<10;i++){
cout<<dp[1][i]<<" ";
}
}
int main(){
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t = 1;
while (t--){
solve();
}
return 0;
}
Python 的处理速度虽然像杂鱼爬行一样慢,但写起来倒是挺省事的❤。
import sys
# 这种递归深度设置,杂鱼是不是想把内存撑爆呀?
sys.setrecursionlimit(1145141919)
input = sys.stdin.readline
def read():
line = sys.stdin.readline()
if not line: return []
return list(map(int, line.split()))
mod = 10**9 + 7
def solve():
n_list = read()
if not n_list: return
n = n_list[0]
a = [0] + read()
if n == 1:
# 只有一个数的情况,❤杂鱼❤一定要看清楚题哦
for i in range(10):
print(1 if a[1] % 10 == i else 0, end=' ')
return
for i in range(n + 1):
a[i] %= 10
# 简单的二维数组,❤杂鱼❤能学会吗?
dp = [[0] * 10 for i in range(n + 5)]
dp[n][a[n]] = 1
for i in range(n - 1, 0, -1):
for j in range(10):
if dp[i+1][j] == 0: continue
# 乘法转移
ed = j * a[i] % 10
dp[i][ed] = (dp[i][ed] + dp[i + 1][j]) % mod
# 加法转移
ed = (j + a[i]) % 10
dp[i][ed] = (dp[i][ed] + dp[i + 1][j]) % mod
for i in range(10):
print(dp[1][i], end=' ')
def main():
t = 1
for _ in range(t):
solve()
if __name__ == "__main__":
main()
看完了吗?看完了就赶紧拿着这份代码去提交,❤杂鱼❤别再盯着屏幕发呆了!
这种后缀合并的思路,只要稍微带点脑子就能想到。要是下次还问这种水平的题,翔子真的会忍不住笑出声来的,呵呵❤~ 明白了吗?杂鱼们!
DP. Python 写完 AI 转 C++.
#include <iostream>
#include <vector>
#include <string>
using namespace std;
const int MOD = 1e9 + 7;
const int DIGIT = 10; // 数字0-9的范围
int main() {
ios::sync_with_stdio(false); // 加速输入输出
cin.tie(nullptr);
int n;
cin >> n;
vector<int> arr(n);
if(n == 1){
int x;
cin >> x;
for(int i = 0; i < 10; i++){
int y = 0;
if(x == i) y = 1;
cout << y << ' ';
}
cout << endl;
return 0;
}
// 读取输入并处理:取每个数的最后一位(对应Python的lambda x: int(x[-1]))
for (int i = 0; i < n; ++i) {
string s;
cin >> s;
arr[i] = s.back() - '0'; // 字符串最后一位转数字(char转int)
}
// 定义三维DP数组:dp[i][j][k]
// 维度:[n][DIGIT][DIGIT],初始值全为0
vector<vector<vector<int>>> dp(n, vector<vector<int>>(DIGIT, vector<int>(DIGIT, 0)));
// 初始化dp[0]:前0个数(第一个数)的状态
for (int i = 0; i < DIGIT; ++i) {
int add = (i + arr[0]) % 10;
int mul = (i * arr[0]) % 10;
dp[0][i][add] = (dp[0][i][add] + 1) % MOD;
dp[0][i][mul] = (dp[0][i][mul] + 1) % MOD;
}
// 状态转移:迭代计算dp[i][j][k]
for (int i = 1; i < n; ++i) {
for (int j = 0; j < DIGIT; ++j) {
for (int k = 0; k < DIGIT; ++k) {
// 计算转移的前驱状态
int prev_add = (j + arr[i]) % 10;
int prev_mul = (j * arr[i]) % 10;
// 状态转移:累加前驱状态的结果并取模
dp[i][j][k] = (dp[i-1][prev_add][k] + dp[i-1][prev_mul][k]) % MOD;
}
}
}
// 输出结果:dp[n-2][arr.back()] 的所有元素
for (int k = 0; k < DIGIT; ++k) {
cout << dp[n-2][arr.back()][k] << " ";
}
cout << endl;
return 0;
}