BZOJ2505: tickets

2 篇文章 0 订阅
这篇博客详细介绍了如何利用数位动态规划(DP)解决BZOJ2505问题。作者探讨了状态转移方程F[A][B][C][L][R]的含义,该状态表示当前处理到数字的第A位,前面的数字和为B,上一次的可用贡献为C,并考虑了是否到达了题目设定的上下界。此外,博客还强调了跟踪每个F状态对应的C值的重要性。
摘要由CSDN通过智能技术生成

数位DP
F[A][B][C][L][R]表示当前为第A位 前面的和为B上一次剩下的可用贡献为C
是否到了上下界

同时需要记录一下每一个F对应的C

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cmath>
using namespace std;
char c;
#define ll long long

inline void read(ll &a){a=0;do c=getchar();while(c<'0'||c>'9');while(c<='9'&&c>='0')a=(a<<3)+(a<<1)+c-'0',c=getchar();}

struct Rec
{
    ll L,R,A,B,C;
    ll ans;
    ll res;
    inline friend bool operator !=(Rec a,Rec b){return (a.L^b.L)|(a.R^b.R)|(a.A^b.A)|(a.B^b.B)|(a.C^b.C);}
};
const
    int Mod=1264273;
struct Chain
{Rec op;Chain *next;}*T[Mod];
inline int Hash(Rec a){return ((a.L+a.R*11+a.A*121+a.B*121*11+a.C*121*121)%Mod+Mod)%Mod;}
Chain *Res;
inline void Add(Rec A){int pl=Hash(A);Chain *n=new Chain;n->next=T[pl];T[pl]=n;n->op=A;}
bool Find(Rec A,Chain*C)
{if(!C)return false;
    if(C->op!=A)
        return Find(A,C->next);
    return Res=C,true;}
int LA[1001],RA[1001];
ll top,n;
ll l,r,k;

ll Pow(ll A,ll B)
{
    if(B==0)return 1;
    if(B==1)return A;
    ll P=Pow(A,B>>1);
    return B&1?P*P*A:P*P;
}
ll Ans(ll A,ll B,ll &C,ll L,ll R)
{
    //printf ("%lld %lld %lld %lld %lld\n",A,B,C,L,R);
    //if(R<L)return 0;
//  if(B>=k&&L==-1&&R==10)
//  {
//      return C=0,Pow(10,top-A+1);
//  }
    if(A>top)
        return (B+C>=k)?(C=0,1):(C+=B,0);
    Rec Cur=(Rec){L,R,A,B,C};
    ll AS=C;
    if(Find(Cur,T[Hash(Cur)]))return //printf("%lld %lld %lld %lld %lld %lld\n",top-A+1,B,C,L,R,(Res->op).ans),
        C=(Res->op).res,(Res->op).ans;
    Cur.ans=0;
    if(L!=-1)
        if(L^R)
            Cur.ans+=Ans(A+1,B+L,C,LA[A+1],10);
        else Cur.ans+=Ans(A+1,B+L,C,LA[A+1],RA[A+1]);
    for(int i=L+1;i<R;i++)
        Cur.ans+=Ans(A+1,B+i,C,-1,10);
    if(R!=L)
        if(R!=10)
            Cur.ans+=Ans(A+1,B+R,C,-1,RA[A+1]);
    Cur.res=C;
    Add(Cur);
    return //printf("%lld %lld %lld %lld %lld %lld\n",top-A+1,B,AS,L,R,Cur.ans),
    Cur.ans;
}

int main()
{   
    read(l),read(r),read(k);
    top=1,n=1;
    while(n<=r)top++,n=(n<<1)+(n<<3);
    top--,n/=10;
    bool fl=false;
    for(int i=1;i<=top;i++)
    {
        RA[i]=(r/n)%10;
        LA[i]=(l/n)%10; 
        //if(LA[i])fl=true;
        //if(!fl)LA[i]=-1;
        n/=10;
    }
    ll A=0;
    ll ans=Ans(1,0,A,LA[1],RA[1]);
    printf("%lld\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值