蒟蒻的数位DP专题总结

BZOJ  1026: [SCOI2009]windy数:

题目链接: http://www.lydsy.com/JudgeOnline/problem.php?id=1026

          dp[11][11][2]:dep,pre,f

           要求的性质就是相邻数字差至少是2。
          递归函数的状态设计如下dep,pre,f,分别表示已经枚举到第dep位,他的前一位(更高的位)是pre,f表示大小关系是否已经确定.

///1085422276

#include<bits/stdc++.h>
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std ;
typedef long long ll;
#define mem(a) memset(a,0,sizeof(a))
#define pb push_back
inline ll read()
{
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-')f=-1;ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        x=x*10+ch-'0';ch=getchar();
    }return x*f;
}
//****************************************
const int  N=500000+50;
#define mod 1000000007
#define inf 10000007

int dp[11][11][2],vis[11][11][2],d[11];
int dfs(int dep,int pre,int f) {
    if(dep<0) return 1;
    else if(pre>=0&&vis[dep][pre][f]) return dp[dep][pre][f];
    else {
        int re=0;
        if(pre == -1) {
            re+=dfs(dep-1,-1,f||d[dep]>0);
            if(f) for(int i=1;i<=9;i++) re+=dfs(dep-1,i,1);
            else {
                for(int i=1;i<=d[dep];i++) {
                    re+=dfs(dep-1,i,i<d[dep]);
                }
            }
        }
        else  {
            if(f) {
                for(int i=0;i<10;i++) {
                    if(abs(i-pre)>1) {
                        re+=dfs(dep-1,i,1);
                    }
                }
            }
            else {
                for(int i=0;i<10;i++) {
                    if(i<=d[dep]&&abs(i-pre)>1) {
                        re+=dfs(dep-1,i,i<d[dep]);
                    }
                }
            }
        }
        if(pre>=0) vis[dep][pre][f]=1,dp[dep][pre][f]=re;
        return re;
    }
}
int cal(int n) {
   mem(dp);mem(vis);
   int len=0;
   while(n) {
    d[len++]=n%10;n/=10;
   }
   return dfs(len-1,-1,0);
}
int main() {
    int a,b;
    while(scanf("%d%d",&a,&b)!=EOF) {
        cout<<cal(b)-cal(a-1)<<endl;
    }
   return 0;
}
BZOJ1026

 BZOJ 3209: 花神的数论题:

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3209

             求的是1到N内进制种类数的个数的乘积,显然是数学题嘛。。。。

             仔细想想:我们如果只考虑二进制上01的个数,也就是如果枚举1的个数,将二进制上含有i个1的个数用数位dp求解出来,再算快速幂,就可以得到答案了

///1085422276

#include<bits/stdc++.h>
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std ;
typedef long long ll;
#define mem(a) memset(a,0,sizeof(a))
#define pb push_back
inline ll read()
{
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-')f=-1;ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        x=x*10+ch-'0';ch=getchar();
    }return x*f;
}
//****************************************
const int  N=60+50;
#define mod 10000007
#define inf 10000007
#define maxn 10000

ll dp[65][65][3],vis[65][65][3],d[N],c[N][N],ans[N];;
ll dfs(int dep,int left,int f) {
   if(vis[dep][left][f]) return dp[dep][left][f];
   if(dep+1<left) return 0;if(left==0) return 1;
   ll& re=dp[dep][left][f];
   vis[dep][left][f]=1;
   if(!f) {
       if(d[dep]==0) {
        re+=dfs(dep-1,left,f);
       }
       else {
        re+=dfs(dep-1,left,1);
        re+=dfs(dep-1,left-1,0);
       }
   }
   else {
     re+=c[dep+1][left];
   }//vis[dep][left][f]=1,dp[dep][left][f]=re;
   return re;
}
ll quick_pow(ll n,ll m) {
   ll ret=1;
   while(m) {
     if(m&1) ret=(ret*n)%mod;
     n=(n*n)%mod;
     m>>=1;
   }
   return ret;
}
ll cal(ll n) {
    int len=0;mem(vis);mem(dp);
    while(n) {
        d[len++]=n%2;
        n/=2;
    }
    for (int i=1;i<=len;i++) {
        ans[i]=dfs(len-1,i,0);
    }
    ll A=1;
    for(int i=2;i<=len;i++)A=(A*quick_pow(i,ans[i]))%mod;
    return A;
}
int main(){
       ll n;
       for(int i=0;i<=60;i++) {
        c[i][0]=c[i][i]=1;
        for(int j=1;j<i;j++) {
            c[i][j]=c[i-1][j]+c[i-1][j-1];
        }
       }
       while(scanf("%lld",&n)!=EOF) {
        printf("%lld\n",cal(n));
       }
  return 0;
}
BZOJ3209

BZOJ 1799: [Ahoi2009]self 同类分布:

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1799

              因为最多18位,最大也就是18*9,我们枚举位数和,去数位dp就可以了

///1085422276

#include<bits/stdc++.h>
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std ;
typedef long long ll;
#define mem(a) memset(a,0,sizeof(a))
#define pb push_back
inline ll read()
{
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-')f=-1;ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        x=x*10+ch-'0';ch=getchar();
    }return x*f;
}
//****************************************
const int  N=600+50;
#define mod 10000007
#define inf 10000007
#define maxn 10000

ll dp[20][200][200][2],vis[20][200][200][2];
int d[N];
ll dfs(int dep,int T,int f,int sum,int M) {
   if(dep<0) return T == 0 && sum == M;
   if(sum>M)return 0;
   if(vis[dep][T][sum][f]) return dp[dep][T][sum][f];
   ll& re=dp[dep][T][sum][f];
   vis[dep][T][sum][f]=1;
   if(f) {
     for(int i=0;i<=9;i++) {
        re+=dfs(dep-1,(T*10+i)%M,f,sum+i,M);
     }
   }
   else {
    for(int i=0;i<=d[dep];i++) {
        re+=dfs(dep-1,(T*10+i)%M,i<d[dep],sum+i,M);
    }
   }
   return re;
}

ll cal(ll x) {
   int len=0;
   ll A=0;
   while(x) d[len++]=x%10,x/=10;
   for(int i=1;i<=9*len;i++) {
        mem(dp),mem(vis);
        A+=dfs(len-1,0,0,0,i);
   }
    return A;
}
int main() {
    ll l,r;
    while(scanf("%I64d%I64d",&l,&r)!=EOF) {
        printf("%I64d\n",cal(r)-cal(l-1));
        //cout<<cal(r)-cal(l-1)<<endl;
    }
    return 0;
}
BZOJ1799

 HDU 4734 F(x):

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4734

              我们先求出F(A),题意只要求满足F(x)<=F(A) 的就行,所以我们从构造数位dp  将F(A)设为上限,

              F(A)可以发现最大就是9*2^9多一点,我们再枚举一个数位大小的时候,将剩余上限减去使用当前数位的F()贡献

              最后也就是枚举完,剩余>=0就可行

///1085422276

#include<bits/stdc++.h>
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std ;
typedef long long ll;
#define mem(a) memset(a,0,sizeof(a))
#define pb push_back
inline ll read()
{
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-')f=-1;ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        x=x*10+ch-'0';ch=getchar();
    }return x*f;
}
//****************************************
const int  N=6000+50;
#define mod 10000007
#define inf 10000007
#define maxn 10000

int dp[10][N],vis[10][N],d[20];

int dfs(int dep,int sum,int f) {
    if(sum<0) return 0;
    if(dep<0) return sum>=0;
    if(f&&vis[dep][sum]) return dp[dep][sum];
    if(f) {
        int& re=dp[dep][sum];
        vis[dep][sum]=1;
        for(int i=0;i<=9;i++) {
            re+=dfs(dep-1,sum-i*(1<<dep),f);
        }
        return re;
    }
    else {
        int re=0;
        for(int i=0;i<=d[dep];i++) {
            re+=dfs(dep-1,sum-i*(1<<dep),i!=d[dep]);
        }
        return re;
    }
}
int cal(int A,int B) {
    if(B==0) return 1;
    int sum=0;int len=0;//mem(dp),mem(vis);
    while(A) sum+=(A%10)*(1<<len),len++,A/=10;
    len=0;
    while(B) d[len++]=B%10,B/=10;
    return dfs(len-1,sum,0);
}

int main() {
    int T=read();
    int A,B;
    for(int i=1;i<=T;i++) {
       A=read(),B=read();
        printf("Case #%d: ",i);
        printf("%d\n",cal(A,B));
    }
    return 0;
}
HDU4734

 HDU 3555 Bomb:

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3555

             求1到N内有49相连的数字个数,经典数位;

///1085422276

#include<bits/stdc++.h>
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std ;
typedef long long ll;
#define mem(a) memset(a,0,sizeof(a))
#define pb push_back
inline ll read()
{
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-')f=-1;ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        x=x*10+ch-'0';ch=getchar();
    }return x*f;
}
//****************************************
const int  N=6000+50;
#define mod 10000007
#define inf 10000007
#define maxn 10000

ll dp[60][60],vis[50][60],d[60];
ll dfs(int dep,int pre,int f) {
    if(dep<0) return 1;
    if(f&&vis[dep][pre]) return dp[dep][pre];
    if(f) {
            ll& re=dp[dep][pre];
           vis[dep][pre]=1;
        for(int i=0;i<=9;i++) {
            if(pre!=4||(pre==4&&i!=9)) {
                re+=dfs(dep-1,i,f);
            }
        }return re;
    }
    else {
        ll re=0;
        for(int i=0;i<=d[dep];i++) {
            if(pre!=4||(pre==4&&i!=9)) 
            re+=dfs(dep-1,i,i<d[dep]);
        } return re;
    }
}
ll cal(ll x) {
    int len=0;mem(dp),mem(vis);
    while(x) d[len++]=x%10,x/=10;
    return dfs(len-1,0,0);
}
int main() {

    int T=read();
    while(T--) {
        ll n;
        scanf("%I64d",&n);
        cout<<n-cal(n)+1<<endl;
    }
    return 0;
}
HDU3555

 HDU 3709 Balanced Number:

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3709

             我们枚举平衡点就OK,注意0 的次数重复计算了,减去len-1就是答案

///1085422276

#include<bits/stdc++.h>
using namespace std;
#pragma comment(linker, "/STACK:102400000,102400000")
using namespace std ;
typedef long long ll;
#define mem(a) memset(a,0,sizeof(a))
#define pb push_back
inline ll read()
{
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-')f=-1;ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        x=x*10+ch-'0';ch=getchar();
    }return x*f;
}
//****************************************
const int  N=6000+50;
#define mod 10000007
#define inf 10000007
#define maxn 10000

ll dp[20][6000][20],vis[20][6000][20],d[N];
ll dfs(int dep,int D,int mid,int f) {
   if(dep<0) return D==3000;
   if(f&&vis[dep][D][mid]) return dp[dep][D][mid];
   if(f) {
     vis[dep][D][mid]=1;
     ll& re=dp[dep][D][mid];
     for(int i=0;i<=9;i++) {
        re+=dfs(dep-1,D-(dep-mid)*i,mid,f);
     }
     return re;
   }
   else {
    ll re=0;
    for(int i=0;i<=d[dep];i++) {
        re+=dfs(dep-1,D-(dep-mid)*i,mid,i<d[dep]);
    }
    return re;
   }

}
ll cal(ll x) {
   if(x==0) return 1;
   if(x<0) return 0;
   int len=0;ll re=0;mem(dp),mem(vis);
   while(x) d[len++]=x%10,x/=10;
   for(int i=0;i<len;i++) {
     re+=dfs(len-1,3000,i,0);
   }
   return re-len+1;
}
int main() {

    int T=read();
    while(T--) {
            ll l,r;
      scanf("%I64d%I64d",&l,&r);
      cout<<cal(r)-cal(l-1)<<endl;
    }
    return 0;
}
HDU3709

转载于:https://www.cnblogs.com/zxhl/p/4954444.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值