码迷,mamicode.com
首页 > 其他好文 > 详细

清北学堂模拟赛d2t6 分糖果(candy)

时间:2017-10-03 23:34:13      阅读:450      评论:0      收藏:0      [点我收藏+]

标签:复杂   const   i++   pre   包含   为我   out   scan   就会   

题目描述
总共有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)

标签:复杂   const   i++   pre   包含   为我   out   scan   就会   

原文地址:http://www.cnblogs.com/zbtrs/p/7624720.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!