zoj4100 Balanced Number 数位DP

题目的意思是询问[A,B]区间内,有多少个Balanced Number,每个Balanced Number符合以下条件:

将一个数按位拆分,存在一个位置mid,使得sigma(val[i]*(mid-i))=sigma(val[j]*(j-mid)) {i<mid,j>mid}

然后就是一脸数位dp的样子...

一开始的想法是枚举mid,因为分析了一下,除0以为的某个Balanced Number,一定只有一个位置符合条件。

然后接下来就脑残了,将数字拆成两半来dp,然后合并的时候死活合并不了。后来想了一下,瞬间觉得自己脑残了,根本就不用拆分。直接从后往前dp就行,没想好代码怎么写,就开始敲,真的很忧伤..

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<map>
#include<queue>
#include<iostream>

using namespace std;

#define For(i,forN) for(int i=0;i<forN;i++)
#define sf  scanf
#define pf  printf
#define mp  make_pair

#define _clr(x,y)   memset(x,(y),sizeof(x))

typedef long long LL;

const int Maxn=21,Maxl=1400,Maxe=Maxl;
LL dp[2][Maxn][Maxl];
int num[Maxn],len;

typedef long long LL;
LL A,B;

LL getAns(int mid)
{
    _clr(dp,0);
    dp[0][len][0]=1;//0表示x<=val,1表示x>val
    for(int i=len-1;i>=0;i--)
    {
        int s=0,l=i-mid;
        for(int j=0;j<Maxe;j++)
        {
            for(int k=s;k<10;k++)
            {
                int tj=l*k+j;
                if(tj<Maxe && tj>=0)
                {
                    if(k<num[i])//那么x一定小于val
                    {
                        dp[0][i][tj]+=dp[0][i+1][j]+dp[1][i+1][j];
                    }
                    else if(k>num[i])//那么x一定大于val
                    {
                        dp[1][i][tj]+=dp[0][i+1][j]+dp[1][i+1][j];
                    }
                    else
                    {
                        dp[0][i][tj]+=dp[0][i+1][j];
                        dp[1][i][tj]+=dp[1][i+1][j];
                    }
                }

            }
        }
    }
    return dp[0][0][0];
}

LL cal(LL val)
{
    if(val<0)   return 0;
    if(val<10)  return val+1;
    len=0;
    while(val)
    {
        num[len++]=val%10;
        val/=10;
    }
    reverse(num,num+len);
    LL ans=0;
    for(int i=0;i<len;i++)
    ans+=getAns(i);
    return ans-len+1;
}

int main()
{
    int cas;
    cin>>cas;
    while(cas--)
    {
        cin>>A>>B;
        cout<<cal(B)-cal(A-1)<<endl;
    }
}

 

转载于:https://www.cnblogs.com/CooCoo/p/3409171.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值