[BZOJ4467] [JSOI2013]数字理论

题目相当于要求一个数x,满足:
1. D10K1x<D10K
2. x mod D=0
3. digit(x)=P  //digit(x)x
4. digit(x/D)=S

考虑进行数位DP

bool dp[len][sum1][sum2][res]意思是,有一个数W,digit(W div D)=sum1,digit(W)=sum2,W mod D=res,能否通过在W的末尾加len位数字使得符合要求
显然边界为dp[0][S][P][0]=1 dp[0][其他]=0

转移为 dp[len][sum1][sum2][res]=OR9next=0dp[len1][sum1+(res10+next) div D][sum2+next][(res10+next) modD]

找方案时贪心地从高位向地位,在DP数组上爬下去^..^

然而这样状态数是O(K*S*P*D),显然会MLE+TLE

怎么办呢?对!常数优化!
我们可以发现,sum1和res确定时,sum2 mod 9是确定的,于是时间和内存的常数就除了9,然后又有好多无效的状态,如 len9+sum1<S len9+sum2<P ,虽然理论时间复杂度还是O(K*S*P*D*10),大约是90亿,但大多数都是无效状态,拜张爷爷的剪枝技巧所赐,便轻松过去了

#include <cstdio>
#include <assert.h>
int K, S, P, D;
bool dp[100][901][102][9];
int main()
{
    scanf("%d%d%d%d", &K, &S, &P, &D);
    if (S > K * 9 || P > K * 9)
        return puts("-1"), 0;
    if (S * D % 9 != P % 9)
        return puts("-1"), 0;
    dp[0][S][P / 9][0] = 1;
    for (int i = 1; i < K; i++)
    {
        int S_low = S - i * 9, P_low = P - i * 9, MODnine;
        if (S_low < 0)
            S_low = 0;
        if (P_low < 0)
            P_low = 0;
        for (int j = S_low; j <= S; j++)
        {
            bool (*DP)[9] = dp[i][j];
            for (int l = 0; l < D; l++)
            {
                MODnine = (j * D + l) % 9;
                for (int k = P_low / 9, realk = k * 9 + MODnine; realk <= P; k++, realk += 9)
                    for (int di = 0; !DP[k][l] && di < 10; di++)
                        if (dp[i - 1][j + (l * 10 + di) / D][(realk + di) / 9][(l * 10 + di) % D])
                            DP[k][l] = 1;
            }
        }
    }
    int head = D;
    while (head < 10 * D && !dp[K - 1][head / D][(head / 10 + head % 10) / 9][head % D])
        head++;
    if (head == 10 * D)
        puts("-1");
    else
    {
        int res = head % D;
        putchar(head / D + 48);
        int I = K - 1, J = head / D, K = head / 10 + head % 10, L = head % D, di;
        int newI, newJ, newK, newL;
        while (I)
        {
            for (di = 0; !dp[I - 1][J + (L * 10 + di) / D][(K + di) / 9][(L * 10 + di) % D]; di++)
                "Nothing to do..";
            putchar((res * 10 + di) / D + 48);
            res = (res * 10 + di) % D;
            newI = I - 1, newJ = J + (L * 10 + di) / D, newK = K + di, newL = (L * 10 + di) % D;
            I = newI, J = newJ, K = newK, L = newL;
        }
        assert(res == 0);
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值