【数位DP】CF55D BZOJ3329 HDU4352 SGU390 HDU5519
来源:互联网 发布:淘宝信用贷款还款 编辑:程序博客网 时间:2024/06/05 07:38
前言
有一些题之前已经写了题解了,就只留一个链接吧…
一般的数位DP都是计算一段区间满足某条件的数有多少个。
顾名思义数位DP就是按照数一位一位滴进行DP。通常至少有二维,其中一位表示当前在第
具体实现方法通常有递推版和记忆化搜索版。
SPOJ10606
SPOJ10606
BZOJ3629
BZOJ3629
CodeForces55D
CodeForces-55D
题目大意:题目大意:给定
比如说
由于被非零数位整除这一个条件很讨厌,尝试将所有可能的数位转化成同一个数整除。这个数就是
然后我们发现
然后
#include <iostream>#include <cstdio>#include <cstring>#define LL long long int#define mod 2520using namespace std;LL f[20][mod+5][50];int h[mod+5], w[20], len, cnt;int gcd(int a,int b){ if(!b)return a; return gcd(b,a%b);}int lcm(int a,int b){ if(!b)return a; return a*b/gcd(a,b);}LL dfs(int i,int l,int rest,bool flag){ if(i==0)return rest%l==0; if(!flag&&~f[i][rest][h[l]])return f[i][rest][h[l]]; int j; LL ans=0; if(flag)j=w[i]; else j=9; for(;j>=0;--j) ans+=dfs(i-1,lcm(l,j),(rest*10+j)%mod,flag&&j==w[i]); if(!flag)f[i][rest][h[l]]=ans; return ans;}//可能有人会问:为什么在计算时f数组不清零?//其实木有必要清零啊,每一次都是从1到9,每一次算的答案都会是一样的LL cal(LL n){ if(n==0)return 1; len=0; while(n){w[++len]=n%10;n/=10;} return dfs(len,1,0,1);}void init(){ memset(f,-1,sizeof f); for(int i=1;i<=mod;++i) if(mod%i==0) h[i]=++cnt;}int main(){ init(); int cas; LL l, r; scanf("%d",&cas); while(cas--) { scanf("%I64d%I64d",&l,&r); printf("%I64d\n",cal(r)-cal(l-1)); } return 0;}
BZOJ3329
BZOJ3329
这样的
将
然后令
于是乎第一问解决。
第二问:
令
然后把两个式子合并一下就有:
这不是Fibonacci sequence?
矩阵加速上吧…
#include <iostream>#include <cstdio>#include <cstring>#define LL long long int#define mod 1000000007using namespace std;int len, w[65];struct mat{ LL num[5][5]; void init(){memset(num,0,sizeof num);} mat operator * (const mat &a)const { mat ans; ans.init(); for(int i=1;i<=2;++i) for(int j=1;j<=2;++j) for(int k=1;k<=2;++k) ans.num[i][j]=(ans.num[i][j]+num[i][k]*a.num[k][j])%mod; return ans; }}a, b;mat power(mat a,LL pos){ mat ans=a; while(pos) { if(pos&1)ans=ans*a; a=a*a; pos>>=1; } return ans;}LL f[65][2][2], n;LL solve1(LL n){ if(n==0)return 0; len=0; while(n){w[++len]=n&1;n>>=1;} memset(f,0,sizeof f); f[len][0][0]=1, f[len][1][1]=1; for(int i=len-1;i;--i) { for(int j=0;j<2;++j) { for(int k=0;k<2;++k) if(!k||!j) { if(w[i]>k)f[i][k][0]+=f[i+1][j][1]; else if(w[i]==k)f[i][k][1]+=f[i+1][j][1]; f[i][k][0]+=f[i+1][j][0]; } } } return f[1][0][1]+f[1][0][0]+f[1][1][0]+f[1][1][1]-1;}LL solve2(LL n){ b.num[1][1]=b.num[1][2]=1; b=b*power(a,n-1); return b.num[1][2];}int main(){ a.num[1][2]=a.num[2][1]=a.num[2][2]=1; int cas; scanf("%d",&cas); while(cas--) { scanf("%lld",&n); printf("%lld\n%lld\n",solve1(n),solve2(n)); } return 0;}
HDU 4352
HDU4352
题目大意:定义一个数字的
考虑最长上升子序列的
维护一个类似于单调栈的东西,插入元素
令
然后用记忆化搜索吧,还不能理解的就看代码和注释吧…
#include <iostream>#include <cstdio>#include <cstring>#define LL long long intusing namespace std;LL l, r, k, f[25][2048][11];int len, w[25];//单调栈中插入元素的操作,删除某个数的标记,添加元素j的标记int get(int s,int j){ for(int l=j;l<10;++l) if(s&(1<<l)){s^=1<<l;break;} s|=1<<j; return s;}//is_zero用来统计前导零的LL dfs(int pos,int s,bool flag,bool is_zero){ if(pos==0) { int cnt=0; //统计有多少个数在单调栈中出现了 while(s&&cnt<=k) { if(s&1)++cnt; s>>=1; } return cnt==k; } if(!flag&&~f[pos][s][k])return f[pos][s][k]; LL ans=0; int j; if(flag)j=w[pos]; else j=9; for(l;j>=0;--j) if(is_zero&&j==0) ans+=dfs(pos-1,0,flag&&j==w[pos],1); else ans+=dfs(pos-1,get(s,j),flag&&j==w[pos],0); if(!flag)f[pos][s][k]=ans; return ans;}LL cal(LL n){ if(n==0)return 0; len=0; while(n){w[++len]=n%10;n/=10;} return dfs(len,0,1,1);}int main(){ memset(f,-1,sizeof f); int cas=0, t; scanf("%d",&t); while(t--) { scanf("%I64d%I64d%I64d",&l,&r,&k); printf("Case #%d: %I64d\n",++cas,cal(r)-cal(l-1)); } return 0;}
SGU390
SGU390
题目大意:有一位售票员给乘客售票,对于每位乘客,他会卖出多张连续的票,直到已卖出的编号的所有位置上的数的和不小于给定的正数
首先此题和上面的题不一样的地方在于不能直接计算
把
然后枚举第
#include <iostream>#include <cstdio>#define LL long long intusing namespace std;LL l, r;int k, w[20], len, w2[20], len2;struct node{ LL cnt, rem; node(){cnt=-1, rem=0;} node(const LL &a,const LL &b){cnt=a, rem=b;} void operator += (const node &a){ cnt+=a.cnt; rem=a.rem; }}dp[19][205][1005];//sum表示目前的数位之和,rem表示剩余容量node dfs(int pos,LL sum,LL rem,bool f,bool f2){ if(!pos) { if(sum+rem>=k)return node(1,0); return node(0,sum+rem); } if(!f&&!f2&&~dp[pos][sum][rem].cnt)return dp[pos][sum][rem]; node ans(0,rem); int e, j; if(f)j=w[pos]; else j=0; if(f2)e=w2[pos]; else e=9; for(;j<=e;++j) ans+=dfs(pos-1,sum+j,ans.rem,f&&j==w[pos],f2&&j==w2[pos]); if(!f&&!f2)dp[pos][sum][rem]=ans; return ans;}LL cal(LL l,LL r){ len=0; while(l){w[++len]=l%10;l/=10;} len2=0; while(r){w2[++len2]=r%10;r/=10;} for(int i=len+1;i<=len2;++i)w[i]=0; return dfs(len2,0,0,1,1).cnt;}int main(){ scanf("%I64d%I64d%d",&l,&r,&k); printf("%I64d\n",cal(l,r)); return 0;}
ZOJ2599
ZOJ2599
HDU5519
HDU5519
题目大意:给定
不含前导零太可恨辣,于是乎令
那么
为了计算
有:
解释:对于出现过的数字都可以再出现一次。
解释:对于一个没出现的数
#include <iostream>#include <cstdio>#include <cstring>#define mod 1000000007#define MAXN 15005#define LL long long intusing namespace std;const int END=1<<5;int n, dp[MAXN][END+5], a[10];LL fac[MAXN], ni[MAXN], cnt[END+5];void init(){ fac[0]=fac[1]=ni[0]=ni[1]=1; for(int i=2;i<=15000;++i) { fac[i]=fac[i-1]*i%mod; ni[i]=-mod/i*ni[mod%i]%mod; if(ni[i]<0)ni[i]+=mod; } for(int i=2;i<=15000;++i)ni[i]=ni[i]*ni[i-1]%mod; //cnt[i]表示i中有几个1 for(int i=1;i<END;++i)cnt[i]=cnt[i^(i&(-i))]+1;}LL c(int n,int m){return fac[n]*ni[m]%mod*ni[n-m]%mod;}int solve(){ memset(dp,0,sizeof dp); for(int s=0;s<END;++s) if(cnt[s^(END-1)]&1)dp[0][s]=mod-1; else dp[0][s]=1; for(int i=1;i<=n;++i) { for(int s=0;s<END;++s) { dp[i][s]=(dp[i][s]+dp[i-1][s]*cnt[s]%mod)%mod; for(int k=0;k<5;++k) if(!((s>>k)&1)&&a[k]<i) dp[i][s|(1<<k)]=(dp[i][s|(1<<k)]+dp[i-1-a[k]][s]*c(i-1,a[k]))%mod; } } return dp[n][END-1];}int work(){ int ans=solve(); if(a[0]>0) { --n, --a[0]; ans=(ans-solve()+mod)%mod; } return ans;}int main(){ init(); int t, cas=0; scanf("%d",&t); while(t--) { scanf("%d",&n); for(int i=0;i<5;++i)scanf("%d",&a[i]); printf("Case #%d: %d\n",++cas,work()); } return 0;}
- 【数位DP】CF55D BZOJ3329 HDU4352 SGU390 HDU5519
- [SGU390]Tickets && 数位DP
- BZOJ3329 Xorequ(数位DP)
- hdu4352(数位DP)
- HDU5519-数位DP或者FFT
- 数位DP HDU3555+CF55D+HDU2089
- hdu4352(数位dp+状态压缩)
- HDU4352 lis+状压+数位dp
- hdu3555+cf55D 数位dp入门题
- CF55D Beautiful numbers (数位DP)
- CF55D:Beautiful numbers(数位dp + 数论)
- 【bzoj3329】Xorequ 矩阵乘法+数位DP
- 【bzoj3329】【Xorequ】【数位dp+矩阵乘法】
- 数位dp+矩阵乘法 【bzoj3329】Xorequ
- 数位DP学习 数位DP板子理解 CF55D解题报告
- XHXJ's LIS HDU4352(数位DP)
- hdu4352 XHXJ's LIS 数位DP
- hdu4352 XHXJ's LIS (数位dp)
- MyVector
- ios开发之抽屉效果图
- title: 如何生成多个ssh并将hexo博客布置到github
- Android Resources$NotFoundException异常
- linux下安装apache(httpd-2.4.3版本)各种坑
- 【数位DP】CF55D BZOJ3329 HDU4352 SGU390 HDU5519
- leetcode 7. Reverse Integer
- leetcode 8. String to Integer (atoi)
- Android本地数据安全尝试(中)——Conceal
- 曲线
- leetcode 9. Palindrome Number
- 326. Power of Three
- 解读Time类
- html-frame框架