Mancala

1|0前言:

感觉难度没有紫吧,因为我在模拟赛场切了耶。

2|0题目描述:

n 个数,第 i 个数为 ai,每次可以选择一个 i 满足 ai=i,并将 ai 赋值为 0,最后你的得分为剩下的数的和,你希望最后得分越小越好。

给出 n,k,你需要求出所有 ai[0,k] 的情况下,你的得分总和与 109+7 取模。

3|0解题思路 & 代码实现:

我的考场思路是这样的,先思考如果给定 a 序列,如何求答案。

模拟之后发现,如果有 ai=i 的话,则一定会操作 i,并且每次一定会选取最前面的 i 进行操作,因为这样一定不会影响其他可行操作位置。

继续思考,发现每个数对答案的具体贡献只和 后面的总操作数 有关。

假设 mi 表示 in 的总操作数,那么可以这么分类讨论:

aii ai>i
贡献 (mi+1+ai)modi mi+1+ai
增加的操作次数 (mi+1+ai)/i 0

具体解释的话,如果 aii 那么它的贡献一定是在操作一定次数当前位置后剩余的数,反之就无法操作,只能一直加上去。

增加的操作次数也是同理。

那么我们就可以写出暴力代码:

#include<bits/stdc++.h> #define int long long using namespace std; const int N = 110, mod = 1e9 + 7; int n, k, a[N], ans; void solve(int id){ if(id > n){ for(int i = n, m = 0; i >= 1; i--){ if(a[i] <= i) ans = (ans + (m + a[i]) % i) % mod; else ans = (ans + m + a[i]) % mod; if(a[i] <= i) m += (m + a[i]) / i; } return; } for(int i = 0; i <= k; i++) a[id] = i, solve(id + 1); } signed main(){ // freopen("stone.in", "r", stdin); // freopen("stone.out", "w", stdout); cin >> n >> k; solve(1); cout << ans << endl; return 0; }

那么我们现在会处理一个序列了,那么可以开始思考正解了。

注意到 n,k100,可以发现 后面的总操作数 的数量级在 5×104 以内。

那么就可以很自然的设置出两个状态。

fi 表示从后往前考虑到 i 的最小得分总和。

gi,j 表示从后往前考虑到 i,操作总数为 j 的方案数。

那么转移具体如下:
(1){fi=fi+fi+1+m=0n×kgi+1,m×((m+j)modi)(ji)fi=fi+fi+1+m=0n×kgi+1,m×(m+j)(j>i)

(2){gi,m+(m+j)/i=gi,m+(m+j)/i+gi+1,m(ji)gi,m=gi,m+gi+1,m(j>i)

那么正解代码如下:

#include<bits/stdc++.h> #define endl '\n' #define int long long using namespace std; const int N = 110, M = 5e4 + 10, mod = 1e9 + 7; int f[N], g[N][M]; signed main(){ // freopen("stone.in", "r", stdin); // freopen("stone.out", "w", stdout); ios::sync_with_stdio(0); cin.tie(0), cout.tie(0); int n, k; cin >> n >> k; g[n + 1][0] = 1; for(int i = n; i >= 1; i--){ for(int j = 0; j <= k; j++){//a[i] for(int m = 0; m <= k * n; m++){//m[i+1] if(j <= i) g[i][m + (m + j) / i] = (g[i][m + (m + j) / i] + g[i + 1][m]) % mod; else g[i][m] = (g[i][m] + g[i + 1][m]) % mod; } } for(int j = 0; j <= k; j++){ if(j <= i){ f[i] = (f[i] + f[i + 1]) % mod; for(int m = 0; m <= k * n; m++) f[i] = (f[i] + g[i + 1][m] * ((m + j) % i) % mod) % mod; }else{ f[i] = (f[i] + f[i + 1]) % mod; for(int m = 0; m <= k * n; m++) f[i] = (f[i] + g[i + 1][m] * ((m + j) % mod) % mod) % mod; } } } cout << f[1] << endl; return 0; }

时间复杂度 O(n2k2)


__EOF__

本文作者huangweiliang
本文链接https://www.cnblogs.com/huangweiliang/p/18549364.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   _huangweiliang  阅读(10)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示