清北学堂模拟赛d2t6 分糖果(candy)
Posted zbtrs
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了清北学堂模拟赛d2t6 分糖果(candy)相关的知识,希望对你有一定的参考价值。
题目描述
总共有n颗糖果,有3个小朋友分别叫做L,Y,K。每个小朋友想拿到至少k颗糖果,但这三个小朋友有一个共同的特点:对3反感。也就是说,如果某个小朋友拿到3颗,13颗,31颗,333颗这样数量的糖果,他就会不开心。(也即它拿到的糖果数量不包含有一位是3)
LYK掌管着这n颗糖果,它想问你有多少种合理的分配方案使得将这n颗糖果全部分给小朋友且没有小朋友不开心。
例如当n=3,k=1时只有1种分配方案,当n=4,k=1时有3种分配方案分别是112,121,211。当n=7,k=2时则不存在任何一种合法的方案。
当然这个答案可能会很大,你只需输出答案对12345647取模后的结果就可以了。
输入格式(candy.in)
第一行两个数表示n,k。
输出格式(candy.out)
一个数表示方案总数。
输入样例
99999 1
输出样例
9521331
对于30%的数据n<=100
对于50%的数据n<=1000。
对于另外30%的数据k=1。
对于100%的数据3<=n<=10^10000,1<=k<=n/3,且n,k不包含前导0。
分析:对于前50%的点,直接暴力枚举+判断就可以了.后50%的点数据和前50%的点数据规模完全不是一个数量级的,肯定要用不同的算法.数字肯定不能在时间复杂度里的,肯定是对数位进行处理,那么就要用到数位dp.
这道题也不是一道特别简单的数位dp,因为要3个数的和等于n,所以我们可以在每一数位的时候枚举3个数上的这一位的值,它们的和与n的第i位相差是≤2的,因为进位最多进两位。同时还有≥k这个限制,所以我们可以设状态f[i][j][kk][l][p]表示前i位,第i+1位要向第i位进j位,kk,l,p表示枚举的3个数是否>k.每次递推的时候就能知道下一个状态,就能够就行转移了.
要求方案数,数字位数又这么多,能想到的算法只有数位dp了,从数字看向数位,是一种很好的思想的转变,如果数位dp有数字大小的限制,那么通用的办法就是加一维表示是否超出限制即可.
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int mod = 12345647; char n[10010], k[10010]; int len1, len2,a[10010],b[10010],f[10010][3][2][2][2],ans; int main() { scanf("%s", n + 1); len1 = strlen(n + 1); scanf("%s", k + 1); len2 = strlen(k + 1); for (int i = 1; i <= len1; i++) a[i] = n[i] - ‘0‘; for (int i = 1; i <= len2; i++) b[i + len1 - len2] = k[i] - ‘0‘; f[0][0][0][0][0] = 1; for (int i = 0; i < len1; i++) for (int j = 0; j <= 2; j++) for (int k = 0; k <= 1; k++) for (int l = 0; l <= 1; l++) for (int p = 0; p <= 1; p++) if (f[i][j][k][l][p]) for (int q = 0; q <= 9; q++) if (q != 3) for (int q2 = 0; q2 <= 9; q2++) if (q2 != 3) for (int q3 = 0; q3 <= 9; q3++) if (q3 != 3) { int ni = i + 1, nj = j * 10 + a[i + 1] - q - q2 - q3; int nk, nl, np; if (nj < 0 || nj > 2) continue; if (k == 0 && q < b[ni]) continue; nk = (k || q > b[ni]); if (l == 0 && q2 < b[ni]) continue; nl = (l || q2 > b[ni]); if (p == 0 && q3 < b[ni]) continue; np = (p || q3 > b[ni]); f[ni][nj][nk][nl][np] += f[i][j][k][l][p]; if (f[ni][nj][nk][nl][np] >= mod) f[ni][nj][nk][nl][np] -= mod; } for (int i = 0; i <= 1; i++) //为什么要枚举0?因为我们排除了<k的情况,0就是=k的情况 for (int j = 0; j <= 1; j++) for (int k = 0; k <= 1; k++) { ans += f[len1][0][i][j][k]; if (ans >= mod) ans -= mod; } printf("%d\n", ans); return 0; }
以上是关于清北学堂模拟赛d2t6 分糖果(candy)的主要内容,如果未能解决你的问题,请参考以下文章