数位dp专辑

来源:互联网 发布:用图表直观表达数据 编辑:程序博客网 时间:2024/05/22 12:15

1.题目链接:codeforces 55D

【题意】一个数能被它每一位的数字整除(0除外)则为beautiful number ,问[l,r]之间有多少个这样的数

【思路】考虑到每一位的数字只能是1,2,,9,最小公倍数为2520

设数w=x*2520+y,每位数字的最小公倍数为mul,则w%mul=(x*2520+y)%mul=x*2520%mul+y%mul=y%mul,只需要记录每一步过后该数对2520取模即可。

dp[len][y][mul]表示处理到第len位时数对2520的模,及当前所有数字的最小公倍数。可开到dp[20][2520][2520]

由于数字的最小公倍数是有限的,可将第三维2520进一步优化,预处理求出2520所有的约数。

优化为dp[20][2520[60]


/***********************************************************************    > File Name: cf55d.cpp    > Author: wanghao    > Mail: haohaoac@163.com     > Created Time: 2015年07月06日 星期一 15时27分54秒 ************************************************************************/#include<iostream>#include<cstring>#include<cstdio>#define ll long long using namespace std;ll dp[20][2520][60];int num[20];int v[60],cnt;int to[2600];int gcd(int a,int b){return b?gcd(b,a%b):a;}int lcm(int a,int b){if(b==0)return a;return a*b/gcd(a,b);}ll dfs(int p,int y,int multi,int flag){//cout<<p<<' '<<y<<' '<<multi<<' '<<flag<<endl;if(p==-1)return y%multi==0;if(!flag&&dp[p][y][to[multi]]!=-1)return dp[p][y][to[multi]] ;ll res=0;int u=flag?num[p]:9;for(int i=0;i<=u;i++){res+=dfs(p-1,(y*10+i)%2520,lcm(multi,i),flag&&i==num[p]);}if(!flag)dp[p][y][to[multi]]=res;//cout<<p<<' '<<y<<' '<<multi<<' '<<flag<<endl;//cout<<res<<endl;return res;}ll work(ll x){int i=0;while(x){num[i++] =x%10;x/=10;}return dfs(i-1,0,1,1) ;}int main(){cnt=0;//memset(to,0,sizeof(to));for(int i=1;i<=2520;i++)//优化第三维{if(2520%i==0){v[cnt]=i;to[i]=cnt++;}}//cout<<cnt<<endl;//for(int i=0;i<cnt;i++)//{//cout<<v[i]<<endl;//}int t;scanf("%d",&t);memset(dp,-1,sizeof(dp));while(t--){ll a,b;cin>>a>>b;cout<<work(b)-work(a-1)<<'\n';}return 0;}


2.题目链接:hdu4352

【题意】定义一个数的值为其数字最长上升子序列的长度,例如3746,最长上升序列为 3,4,6,值为3.求[l,r]个中值为k的数有多少个。

【思路】好题。。忍不住看了题解。。求最长上升子序列时,我们需要维护一个序列,表示长度为1的最后一个数的最小值,长度为2的最后一个数的最小值,长度为3的最后一个的最小值。。。这个序列是单调递增的,我们只需要记录下这个序列即可,只有0-9  10个数字,状态压缩!

/*************************************************************************    > File Name: hdu4352.cpp    > Author: wanghao    > Mail: haohaoac@163.com     > Created Time: 2015年07月09日 星期四 20时19分09秒 ************************************************************************/#include<iostream>#include<cstring>#include<cstdio>#define ll long longusing namespace std;ll dp[20][1<<10][11];int num[20];int k;int getlen(int state){int res=0;for(int i=0;i<10;i++)res+=(state&(1<<i))!=0;return res;}int update(int state,int v){int len=getlen(state);if(len==0&&v==0)return 0;if(len==0)return 1<<v;for(int i=9;i>=0;i--){if(state&(1<<i))if(i<v)return state|(1<<v);else break;}for(int i=0;i<=9;i++){if(state&(1<<i)){if(i>=v)return state^(1<<i)|(1<<v);}}return state;}ll dfs(int p,int state,int len,int flag){//cout<<p<<' '<<state<<' '<<len<<' '<<flag<<endl;if(p==-1)return getlen(state)==len;if(!flag&&dp[p][state][len]!=-1)return dp[p][state][len];if(getlen(state)>len)return dp[p][state][len]=0;ll res=0;int u=flag?num[p]:9;for(int i=0;i<=u;i++){int now=update(state,i);res+=dfs(p-1,now,len,flag&&i==num[p]);}//cout<<p<<' '<<state<<' '<<len<<' '<<flag<<endl;//cout<<res<<endl;if(!flag){dp[p][state][len]=res;}return res;}ll work(ll x){int i=0;while(x){num[i++]=x%10;x/=10;}return dfs(i-1,0,k,1);}int main(){memset(dp,-1,sizeof(dp));int t;scanf("%d",&t);int ca=0;while(t--){ll a,b;scanf("%lld%lld%d",&a,&b,&k);printf("Case #%d: ",++ca);cout<<work(b)-work(a-1)<<endl;}return 0;}

3.题目链接:hdu2089

【题意】含4或62的数为不吉利数,求[l,r]中有多少个吉利的数

【思路】水题。。直接写

<span style="font-size:14px;">/*************************************************************************    > File Name: hdu2089.cpp    > Author: wanghao    > Mail: haohaoac@163.com     > Created Time: 2015年07月06日 星期一 15时58分40秒 ************************************************************************/#include<iostream>#include<cstring>#include<cstdio>using namespace std;int num[10];int len;int dp[10][2];int dfs(int p,int flag,int flag2){if(p==-1)return 1;if(!flag&&dp[p][flag2]!=-1)return dp[p][flag2];int u=flag?num[p]:9;int res=0;for(int i=0;i<=u;i++){if(i==2&&!flag2)continue;if(i==4)continue;res+=dfs(p-1,flag&&i==num[p],i!=6);}return dp[p][flag2]=res;}int work(int x){memset(dp,-1,sizeof(dp));int len=0;while(x){num[len++]=x%10;x/=10;}return dfs(len-1,1,1);}int main(){int n,m;while(cin>>n>>m){if(n==0&&m==0)break;printf("%d\n",work(m)-work(n-1));}return 0;}</span>

4.题目链接:hdu3555

【题意】跟上面一题差不多,求[1,x]中含49的有多少个

   【思路】同样水题,记录前一个数字是不是4

<span style="font-size:14px;">/*************************************************************************    > File Name: hdu3555.cpp    > Author: wanghao    > Mail: haohaoac@163.com     > Created Time: 2015年07月06日 星期一 16时27分38秒 ************************************************************************/#include<iostream>#include<cstring>#include<cstdio>#define ull  unsigned long long #define ll long longusing namespace std;int num[25];ll A[20];long long dp[25][2];long long dfs(int p,int flag,int flag4,int ok){//cout<<p<<" "<<flag<<' '<<flag4<<' '<<ok<<endl;if(p==-1)return ok;if(!flag&&ok){//cout<<"in "<<A[p+1]<<endl;return A[p+1];}if(!flag&&dp[p][flag4]!=-1)return dp[p][flag4];int u=flag?num[p]:9;ll res=0;for(int i=0;i<=u;i++){res+=dfs(p-1,flag&&i==num[p],i==4,ok||(flag4&&i==9));}return dp[p][flag4] = res;}ull work(ull x){memset(dp,-1,sizeof(dp));int i=0;while(x){num[i++]=x%10;x/=10;}return dfs(i-1,1,0,0);}int main(){int t;A[0]=1;for(int i=1;i<=18;i++){A[i]=A[i-1]*10;//cout<<A[i]<<' ';}scanf("%d",&t);while(t--){unsigned long long a;scanf("%I64u",&a);printf("%I64u\n",work(a));//cout<<work(a)<<endl;}return 0;}</span>


5.题目链接:poj3252

【题意】将数化为二进制,0的个数不比1少,求[l,r]中这样的数有多少

【思路】将数转化为2进制,再进行dp,保存当前0,1的个数,注意开头取0的情况

<span style="font-size:14px;">/*************************************************************************    > File Name: poj3252.cpp    > Author: wanghao    > Mail: haohaoac@163.com     > Created Time: 2015年07月06日 星期一 19时23分03秒 ************************************************************************/#include<iostream>#include<cstring>#include<cstdio>using namespace std;int dp[35][35][35][2];int num[35];int dfs(int p,int num0,int num1,int flag){//cout<<p<<' '<<num0<<' '<<num1<<' '<<flag<<endl;if(p==-1)return num0>=num1;if(dp[p][num0][num1][flag]!=-1)return dp[p][num0][num1][flag];int u=flag?num[p]:1;int res=0;for(int i=0;i<=u;i++){res+=dfs(p-1,num1?num0+!i:0,num1+i,flag&&i==num[p]);}//cout<<"ans "<<endl;//cout<<p<<' '<<num0<<' '<<num1<<' '<<flag<<endl;//cout<<res<<endl;return dp[p][num0][num1][flag]=res;}int work(int x){int i=0;while(x){num[i++]=x%2;x/=2;}//for(int j=i-1;j>=0;j--)//cout<<num[j];//cout<<endl;memset(dp,-1,sizeof(dp));return dfs(i-1,0,0,1);}int main(){int a,b;while(scanf("%d%d",&a,&b)!=EOF){printf("%d\n",work(b)-work(a-1));}return 0;}</span>

6.题目链接:hdu3709

【题意】取一个数中的某一数字为支点,左右距离*数字之和相等即为平衡数,例如4139,取3为支点,4*2+1*1=9*1,4139为平衡数求[l,r]间有多少个平衡数

【思路】对于一个数,若其为平衡数,则平衡点必唯一。设取x 点为支点,左侧加权和为Lx,数字和为SUMl;右侧加权和为Rx,数字和为SUMr.该数字为N x,则支点右移一位左侧加权和为Lx+SUMl+Nx,右侧加权和为Rx-SUMr。

观察原左右侧加权和之差,Lx-Rx,现加权和之差,Lx-Rx-(SUMl+Nx+SUMr)=Lx-Rx-sum(所有数字之和)。

发现,对于每一个支点,求得的差都是不一样的,因此平衡点必唯一。若存在平衡点,则Lx-Rx=0,取任意一点为支点,差值必然是sum的倍数!若差值为sum的倍数则必然存在平衡点,最左侧为支点差值<=0,最右侧为支点差值>=0,中间必有平衡点!以最右侧为支点,算出的差值为sum的倍数则该数是平衡数

<span style="font-size:14px;">/*************************************************************************    > File Name: hdu3709.cpp    > Author: wanghao    > Mail: haohaoac@163.com     > Created Time: 2015年07月06日 星期一 20时07分21秒 ************************************************************************/#include<iostream>#include<cstring>#include<cstdio>#define ll long long using namespace std;int num[25];ll dp[2000][180][20];ll dfs(int p,int value,int sum,int flag){//cout<<p<<' '<<value<<' '<<sum<<' '<<flag<<endl;if(p==-1)return sum==0||value%sum==0;if(!flag&&dp[value][sum][p]!=-1)return dp[value][sum][p];int u=flag?num[p]:9;ll res=0;for(int i=0;i<=u;i++){res+=dfs(p-1,value+i*(p+1),sum+i,flag&&i==num[p]);}if(!flag)dp[value][sum][p]=res;//cout<<p<<' '<<value<<' '<<sum<<' '<<flag<<endl;//cout<<res<<endl;return res;}long long work(ll  x){//cout<<"work "<<endl;//cout<<x<<endl;if(x==-1)return 0;int i=0;while(x){num[i++]=x%10;;x/=10;}return dfs(i-1,0,0,1);}int main(){//freopen("a.txt","r",stdin);int t;memset(dp,-1,sizeof(dp));cin>>t;while(t--){long long a,b;cin>>a>>b;if(a>b){int tm=b;b=a;a=tm;}cout<<work(b)-work(a-1)<<'\n';}return 0;}</span>

7.题目链接:hdu3652

【题意】数中出现13,[1,n]中有多少个这样的数

【思路】。。。

<span style="font-size:14px;">/*************************************************************************    > File Name: hdu3652.cpp    > Author: wanghao    > Mail: haohaoac@163.com     > Created Time: 2015年07月07日 星期二 00时18分32秒 ************************************************************************/#include<iostream>#include<cstring>#include<cstdio>using namespace std;int num[15];int dp[12][13][2][2];int dfs(int p,int yu,int flag1,int flag13,int flag){if(p==-1)return yu==0&&flag13;if(!flag&&dp[p][yu][flag1][flag13]!=-1)//flag13也可单列出来,减小一维return dp[p][yu][flag1][flag13];int u=flag?num[p]:9;int res=0;for(int i=0;i<=u;i++){res+=dfs(p-1,(yu*10+i)%13,i==1,flag13||(flag1&&i==3),flag&&i==num[p]);}//cout<<p<<' '<<yu<<' '<<flag1<<' '<<flag13<<' '<<flag<<endl;//cout<<res<<endl;if(!flag)dp[p][yu][flag1][flag13]=res;return res;}int work(int x){int i=0;while(x){num[i++]=x%10;x/=10;}return dfs(i-1,0,0,0,1);}int main(){memset(dp,-1,sizeof(dp));int n;while(scanf("%d",&n)!=EOF){printf("%d\n",work(n));}return 0;}</span>

8.题目链接:hdu4734

【题意】函数  F(x) = A n * 2 n-1 + A n-1 * 2 n-2 + ... + A 2 * 2 + A 1 * 1求[0,b]中f(i)比f(A)小的个数。

【思路】f最大为9*2^9.....+9*2^0=9*(2^10-1)<10*2^10,dp[len][sum]表示剩下长度为len,f值比sum小或相等的个数。奇怪的题目。。。

<span style="font-size:14px;">/*************************************************************************    > File Name: hdu4734.cpp    > Author: wanghao    > Mail: haohaoac@163.com     > Created Time: 2015年07月07日 星期二 00时18分32秒 ************************************************************************/#include<iostream>#include<cstring>#include<cstdio>using namespace std;int num[12];int dp[12][5120];int fa;int f(int x){int res=0;int v=1;while(x){res=res+x%10*v;x/=10;v*=2;}return res;}int A[10];int dfs(int p,int sum,int flag){//cout<<p<<' '<<sum<<' '<<flag<<endl;if(p==-1)return sum>=0;if(sum<0)return 0;if(!flag&&dp[p][sum]!=-1)return dp[p][sum];int u=flag?num[p]:9;int res=0;for(int i=0;i<=u;i++){res+=dfs(p-1,sum-i*A[p],flag&&i==num[p]);}//cout<<p<<' '<<sum<<' '<<flag<<endl;//cout<<res<<endl;if(!flag)dp[p][sum]=res;return res;}int work(int x){if(x==-1)return 0;int i=0;while(x){num[i++]=x%10;x/=10;}//memset(dp,-1,sizeof(dp));return dfs(i-1,fa,1);}int main(){A[0]=1;for(int i=1;i<=9;i++)A[i]=A[i-1]*2;int t;scanf("%d",&t);memset(dp,-1,sizeof(dp));int T=t;while(t--){int a,b;scanf("%d%d",&a,&b);fa=f(a);//cout<<"fa: "<<fa<<endl;printf("Case #%d: %d\n",T-t,work(b));}return 0;}</span>


9.题目链接:hdu4507

【思路】主要还是推平方和的公式  ∑(x+ai)^2=n*x^2+2*x*∑ai+∑ai^2,需要得到子问题的和及平方和及个数。细节地方一定要注意,个数为0与和为0是不一样的,和为0还有可能存在符合条件的数。求个数的时候一定要注意不要取模。。然而取模居然也能AC。。

<span style="font-size:14px;">/*************************************************************************    > File Name: hdu4507.cpp    > Author: wanghao    > Mail: haohaoac@163.com     > Created Time: 2015年07月07日 星期二 20时07分53秒 ************************************************************************/#include<iostream>#include<cstring>#include<cstdio>#define ll long longusing namespace std;int mod=1000000007;ll dp[20][7][7][3];ll A[20];int num[20];void dfs(int p,int sum7,int value7,int flag,ll *sum,ll *sum2,ll *sumn){if(p==-1){*sum=*sum2=0;if(sum7==0||value7==0)*sumn=0;else *sumn=1;return ;}if(!flag&&dp[p][sum7][value7][2]!=-1){*sum=dp[p][sum7][value7][0];*sum2=dp[p][sum7][value7][1];*sumn=dp[p][sum7][value7][2];return ;} int u=flag?num[p]:9;*sumn=*sum=*sum2=0;int ok=0;for(int i=0;i<=u;i++){if(i==7)continue;ll a,b,c;dfs(p-1,(sum7+i)%7,(value7*10+i)%7,flag&&i==num[p],&a,&b,&c);if(c==0)//不判也能ac...但不知道为毛continue;ok=1;*sum2=(*sum2+c%mod*i*A[p]%mod*i*A[p]%mod+b+2*i*A[p]%mod*a%mod)%mod;*sum=(*sum +c%mod*i*A[p]+a)%mod;*sumn=*sumn+c;//别取模。。但取模也能a}//cout<<"df "<<p<<' '<<sum7<<' '<<value7<<' '<<flag<<endl;//cout<<*sum<<' '<<*sum2<<' '<<*sumn<<endl;if(!flag) {dp[p][sum7][value7][0]=*sum;dp[p][sum7][value7][1]=*sum2;dp[p][sum7][value7][2]=*sumn;} }ll work(ll x){//cout<<"work "<<x<<endl;int i=0;while(x){num[i++]=x%10;x/=10;}ll a,b,c;dfs(i-1,0,0,1,&a,&b,&c);//cout<<b<<endl;return b;} int main(){int t;scanf("%d",&t);memset(dp,-1,sizeof(dp));for(int i=0;i<20;i++)A[i]=i==0?1:(A[i-1]*10)%mod;while(t--){ll a,b;cin>>a>>b;cout<<(work(b)-work(a-1)+mod)%mod<<'\n';}return 0;}</span>

PS:就是模板题( ⊙ o ⊙ )啊!

0 0
原创粉丝点击