清北学堂模拟赛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)的主要内容,如果未能解决你的问题,请参考以下文章

清北学堂模拟赛d6t4 数组异或

清北学堂Day1

清北学堂 站军姿

清北学堂Day3

清北学堂Day2

清北学堂D4