原博客https://www.cnblogs.com/HDUjackyan/p/9142156.html
1.(HDOJ2089) http://acm.hdu.edu.cn/showproblem.php?pid=2089
分析:裸模板题
1 #include<cstdio>
2 #include<cstring>
3 #include<algorithm>
4 using namespace std;
5 typedef long long ll;
6 int a[20];
7 int dp[20][2];
8 int dfs(int pos,int pre,int sta,bool limit)
9 {
10 if ( pos==-1 ) return 1;
11 if ( !limit && dp[pos][sta]!=-1 ) return dp[pos][sta];
12 int up=limit?a[pos]:9;
13 int tmp=0;
14 for ( int i=0;i<=up;i++ )
15 {
16 if ( pre==6 && i==2 ) continue;
17 if ( i==4 ) continue;
18 tmp+=dfs(pos-1,i,i==6,limit&&i==a[pos]);
19 }
20 if ( !limit ) dp[pos][sta]=tmp;
21 return tmp;
22 }
23
24 int solve(int x)
25 {
26 int pos=0;
27 while ( x )
28 {
29 a[pos++]=x%10;
30 x/=10;
31 }
32 return dfs(pos-1,-1,0,true);
33 }
34
35 int main()
36 {
37 int l,r;
38 while ( scanf("%d%d",&l,&r)!=EOF && (l+r) )
39 {
40 memset(dp,-1,sizeof(dp));
41 printf("%d\n",solve(r)-solve(l-1));
42 }
43 return 0;
44 }
2.(HDOJ3555) http://acm.hdu.edu.cn/showproblem.php?pid=3555
题意:求区间内不出现49的数的个数
分析:裸模板题
1 #include<cstdio>
2 #include<cstring>
3 #include<algorithm>
4 using namespace std;
5 typedef long long ll;
6 int a[20];
7 ll dp[20][2];
8 ll dfs(int pos,int pre,int sta,bool limit)
9 {
10 if ( pos==-1 ) return 1;
11 if ( !limit && dp[pos][sta]!=-1 ) return dp[pos][sta];
12 int up=limit?a[pos]:9;
13 ll tmp=0;
14 for ( int i=0;i<=up;i++ )
15 {
16 if ( pre==4 && i==9 ) continue;
17 tmp+=dfs(pos-1,i,i==4,limit&&i==a[pos]);
18 }
19 if ( !limit ) dp[pos][sta]=tmp;
20 return tmp;
21 }
22
23 ll solve(ll x)
24 {
25 int pos=0;
26 while ( x )
27 {
28 a[pos++]=x%10;
29 x/=10;
30 }
31 return dfs(pos-1,-1,0,true);
32 }
33
34 int main()
35 {
36 ll l,r,T;
37 memset(dp,-1,sizeof(dp));
38 scanf("%lld",&T);
39 while ( T-- )
40 {
41 scanf("%lld",&r);
42 printf("%lld\n",r-solve(r)+1);
43 }
44 return 0;
45 }
3.(HDOJ4734) http://acm.hdu.edu.cn/showproblem.php?pid=4734
题意:题目给了个f(x)的定义:F(x) = An * 2n-1 + An-1 * 2n-2 + ... + A2 * 2 + A1 * 1,Ai是十进制数位,然后给出a,b求区间[0,b]内满足f(i)<=f(a)的i的个数。
分析:采用相减的思想,dp[i][j],第一维表示处于数字的第几位(即pos),第二维表示是枚举到当前pos位,后面位数最多能凑的权值和为j(起始值为f(a)),最后当j>=0是满足条件的数。具体解释见上面的博客
1 #include<cstdio>
2 #include<cstring>
3 #include<algorithm>
4 using namespace std;
5 typedef long long ll;
6 const int maxn=10010;
7 int a[12];
8 int dp[12][maxn];
9 int sum;
10 int pow_[maxn];
11
12 int dfs(int pos,int sta,bool limit)
13 {
14 if ( pos==-1 ) return 1;
15 if ( !limit && dp[pos][sta]!=-1 ) return dp[pos][sta];
16 int up=limit?a[pos]:9;
17 int tmp=0;
18 for ( int i=0;i<=up;i++ )
19 {
20 int x=pow_[pos]*i;
21 if ( sta-x<0 ) continue;
22 tmp+=dfs(pos-1,sta-x,limit&&i==a[pos]);
23 }
24 if ( !limit ) dp[pos][sta]=tmp;
25 return tmp;
26 }
27
28 int solve(int x)
29 {
30 int pos=0;
31 while ( x )
32 {
33 a[pos++]=x%10;
34 x/=10;
35 }
36 return dfs(pos-1,sum,true);
37 }
38
39 int main()
40 {
41 int l,r,T,i,j,k,h,A,B,x,y,z,cnt;
42 pow_[0]=1;
43 for ( i=1;i<=8;i++ ) pow_[i]=pow_[i-1]*2;
44 scanf("%d",&T);
45 memset(dp,-1,sizeof(dp));
46 for ( h=1;h<=T;h++ )
47 {
48 scanf("%d%d",&A,&B);
49 sum=0;
50 cnt=0;
51 x=A;
52 while ( x )
53 {
54 y=x%10;
55 sum+=y*pow_[cnt++];
56 x/=10;
57 }
58 printf("Case #%d: %d\n",h,solve(B));
59 }
60 return 0;
61 }
4.(POJ3252) http://poj.org/problem?id=3252
题意:求一个范围内满足,二进制下0的位数>=1的位数的个数
分析:将原先的十进制一位转化为二进制一位.此题要在dfs中添加bool型的lead表示前导0,因为这题需要拿0的个数和1的个数比较,所以需要考虑前导0.同时dp数组的第二维记录的是0的个数-1的个数。因为可能为负,所以初始值不为0,而记一个较大的数(我记的是32,只要能使得过程不为负即可)
1 #include<cstdio>
2 #include<cstring>
3 #include<algorithm>
4 using namespace std;
5 typedef long long ll;
6 int a[50];
7 ll dp[50][66];
8 ll dfs(int pos,int sta,bool lead,bool limit)
9 {
10 if ( pos==-1 ) return sta>=32;
11 if ( !limit && !lead && dp[pos][sta]!=-1 ) return dp[pos][sta];
12 int up=limit?a[pos]:1;
13 ll tmp=0;
14 for ( int i=0;i<=up;i++ )
15 {
16 if(lead && i==0) tmp+=dfs(pos-1,sta,lead,limit && i==a[pos]);
17 else tmp+=dfs(pos-1,sta+(i==0?1:-1),lead && i==0,limit && i==a[pos]);
18 }
19 if ( !limit&&!lead ) dp[pos][sta]=tmp;
20 return tmp;
21 }
22
23 ll solve(ll x)
24 {
25 int pos=0;
26 while ( x )
27 {
28 a[pos++]=x%2;
29 x/=2;
30 }
31 return dfs(pos-1,32,true,true);
32 }
33
34 int main()
35 {
36 ll l,r;
37 memset(dp,-1,sizeof(dp));
38 while ( scanf("%lld%lld",&l,&r)!=EOF )
39 {
40 printf("%lld\n",solve(r)-solve(l-1));
41 }
42 return 0;
43 }
5.(HDOJ5179) http://acm.hdu.edu.cn/showproblem.php?pid=5179
题意:给定一个数A,a数组从0开始对应着数A从左到右的每一尾,现在要求数A右边的数都不比左边的数大,同时要求在左边的数去模右边的数都为0
分析:dp数组的第二维记录前一位数是多少即可。转移时要转移到比前一位数小的数同时要被前一位数取模为0的数
1 #include<cstdio>
2 #include<cstring>
3 #include<algorithm>
4 using namespace std;
5 typedef long long ll;
6 int a[20];
7 int dp[20][12];
8 int dfs(int pos,int sta,bool lead,bool limit)
9 {
10 if ( pos==-1 ) return 1;
11 if ( !limit && !lead && dp[pos][sta]!=-1 ) return dp[pos][sta];
12 int up=limit?a[pos]:9;
13 int tmp=0;
14 for ( int i=0;i<=up;i++ )
15 {
16 if ( !lead&&i==0 ) continue;
17 if ( lead )
18 {
19 tmp+=dfs(pos-1,i,lead && i==0,limit&&i==a[pos]);
20 continue;
21 }
22 if ( i>sta&&sta!=-1 ) break;
23 if ( sta%i!=0 && sta!=-1 ) continue;
24 tmp+=dfs(pos-1,i,lead && i==0,limit&&i==a[pos]);
25 }
26 if ( !limit&&!lead ) dp[pos][sta]=tmp;
27 return tmp;
28 }
29
30 int solve(int x)
31 {
32 int pos=0;
33 while ( x )
34 {
35 a[pos++]=x%10;
36 x/=10;
37 }
38 return dfs(pos-1,-1,true,true);
39 }
40
41 int main()
42 {
43 int l,r,T;
44 scanf("%d",&T);
45 memset(dp,-1,sizeof(dp));
46 while ( T-- )
47 {
48 scanf("%d%d",&l,&r);
49 printf("%d\n",solve(r)-solve(l-1));
50 }
51 return 0;
52 }
6.(HDOJ3652) http://acm.hdu.edu.cn/showproblem.php?pid=3652
题意:给定一个范围,求该范围内不含13同时不是13倍数的数的个数
分析:设置三维数组dp[i][j][k],第一维表示位置,第二维表示13的余数,
第三维有三个值,0代表此前还未出现过13同时前一位不为1,1代表此前还未出现过13同时前1位位1,2代表此前已经出现过了13
最后判断时,只有当第二维为0,第三维为2时才加入计数
注意第三维转移时到底是多少
1 #include<cstdio>
2 #include<cstring>
3 #include<algorithm>
4 using namespace std;
5 typedef long long ll;
6 int a[20];
7 int dp[20][15][3];
8 int dfs(int pos,int rem,int sta,bool limit)
9 {
10 if ( pos==-1 ) return rem==0&&sta==2;
11 if ( !limit && dp[pos][rem][sta]!=-1 ) return dp[pos][rem][sta];
12 int up=limit?a[pos]:9;
13 int tmp=0;
14 for ( int i=0;i<=up;i++ )
15 {
16 int x=sta;
17 if ( sta==0 && i==1 ) x=1;
18 if ( sta==1 && i!=1 ) x=0;
19 if ( sta==1 && i==3 ) x=2;
20 tmp+=dfs(pos-1,(rem*10+i)%13,x,limit&&i==a[pos]);
21 }
22 if ( !limit ) dp[pos][rem][sta]=tmp;
23 return tmp;
24 }
25
26 int solve(int x)
27 {
28 int pos=0;
29 while ( x )
30 {
31 a[pos++]=x%10;
32 x/=10;
33 }
34 return dfs(pos-1,0,0,true);
35 }
36
37 int main()
38 {
39 int l,r;
40 memset(dp,-1,sizeof(dp));
41 while ( scanf("%d",&r)!=EOF )
42 {
43 printf("%d\n",solve(r));
44 }
45 return 0;
46 }
7.(HDOJ3709) http://acm.hdu.edu.cn/showproblem.php?pid=3709
题意:求区间[L, R]内平衡数的个数 平衡数的定义是指,以某位作为支点,此位的左面(数字 * 距离)之和 与右边相等,距离是指某位到支点的距离;
分析:首先需要明确一个数如果是平衡数,那么它的支点一定是确定。所以需要枚举支点是哪个点。要注意在数位dp的dfs中,由于 0 对于每个位置都会被统计到,最后要再减去重复的。
1 #include<cstdio>
2 #include<cstring>
3 #include<algorithm>
4 using namespace std;
5 typedef long long ll;
6 int a[20];
7 ll dp[20][20][1800];
8 ll dfs(int pos,int bal,int sum,bool limit)
9 {
10 if ( pos==-1 ) return sum==0;
11 if ( !limit && dp[pos][bal][sum]!=-1 ) return dp[pos][bal][sum];
12 int up=limit?a[pos]:9;
13 ll tmp=0;
14 for ( int i=0;i<=up;i++ )
15 {
16 if ( sum+(pos-bal)*i<0 ) continue;
17 tmp+=dfs(pos-1,bal,sum+(pos-bal)*i,limit&&i==a[pos]);
18 }
19 if ( !limit ) dp[pos][bal][sum]=tmp;
20 return tmp;
21 }
22
23 ll solve(ll x)
24 {
25 if ( x==-1 ) return 0;
26 int pos=0;
27 while ( x )
28 {
29 a[pos++]=x%10;
30 x/=10;
31 }
32 ll ans=0;
33 for ( int i=0;i<pos;i++ ) ans+=dfs(pos-1,i,0,true);
34 return ans-pos+1;
35 }
36
37 int main()
38 {
39 ll l,r;
40 int T;
41 memset(dp,-1,sizeof(dp));
42 scanf("%d",&T);
43 while ( T-- )
44 {
45 scanf("%I64d%I64d",&l,&r);
46 printf("%I64d\n",solve(r)-solve(l-1));
47 }
48 return 0;
49 }
8.(HDOJ4507) http://acm.hdu.edu.cn/showproblem.php?pid=4507
分析:dp[i][j][k]第二维表示%7后的余数,第三维表示所有位数的和%7后的值。因为要求的是所有满足条件的数的平方和,我们考虑对一个数按平方和规则进行拆分。
结构体数组dp中记录三个值,cnt代表满足条件的数的个数,sum表示满足条件的数的求和,sum2表示满足条件的数的平方和.
以下转移式为不带取模的转移方程,假设第i位是当前这位,pos为当前所处的位置,p[pos]代表10^pos次,当前的状态为ans,子状态为tmp(方法:先考虑单个式子再进行累加)
ans.cnt+=tmp.cnt
ans.sum+=(tmp.sum+tmp.cnt*(i*p[pos]))
ans.sum2+=(tmp.cnt*((i*p[pos])^2)+2*(i*p[i])*tmp.sum+tmp.sum2)
1 #include<cstdio>
2 #include<cstring>
3 #include<algorithm>
4 using namespace std;
5 typedef long long ll;
6 const ll mod=1e9+7;
7 int a[20];
8 ll p[20];
9 struct node{
10 ll cnt,sum,sum2;
11 node(ll _cnt=0,ll _sum=0,ll _sum2=0):cnt(_cnt),sum(_sum),sum2(_sum2) {}
12 }dp[20][10][10];
13
14 node dfs(int pos,int rem,int snum,bool limit)
15 {
16 if ( pos==-1 )
17 {
18 node ss(0,0,0);
19 if ( rem!=0 && snum!=0 ) ss.cnt=1;
20 return ss;
21 }
22 if ( !limit && dp[pos][rem][snum].sum2!=-1 ) return dp[pos][rem][snum];
23 int up=limit?a[pos]:9;
24 node ans(0,0,0);
25 for ( int i=0;i<=up;i++ )
26 {
27 if ( i==7 ) continue;
28 node tmp=dfs(pos-1,(rem*10+i)%7,(snum+i)%7,limit&&i==a[pos]);
29 ans.cnt+=tmp.cnt;
30 ans.cnt%=mod;
31 ans.sum+=(tmp.sum+i*p[pos]%mod*tmp.cnt%mod)%mod;
32 ans.sum%=mod;
33 ans.sum2+=(tmp.sum2+2*p[pos]*i%mod*tmp.sum%mod)%mod;
34 ans.sum2%=mod;
35 ans.sum2+=(tmp.cnt*p[pos]%mod*p[pos]%mod*i*i%mod);
36 ans.sum2%=mod;
37 }
38 if ( !limit ) dp[pos][rem][snum]=ans;
39 return ans;
40 }
41
42 ll solve(ll x)
43 {
44 int pos=0;
45 while ( x )
46 {
47 a[pos++]=x%10;
48 x/=10;
49 }
50 node tmp=dfs(pos-1,0,0,true);
51 return tmp.sum2;
52 }
53
54 int main()
55 {
56 ll l,r;
57 int T;
58 memset(dp,-1,sizeof(dp));
59 p[0]=1;
60 for ( int i=1;i<=18;i++ ) p[i]=(p[i-1]*10)%mod;
61 scanf("%d",&T);
62 while ( T-- )
63 {
64 scanf("%I64d%I64d",&l,&r);
65 printf("%I64d\n",((solve(r)-solve(l-1))%mod+mod)%mod);
66 }
67 return 0;
68 }
9.(HDOJ3886)http://acm.hdu.edu.cn/showproblem.php?pid=3886
题意:给出一个字符,只含'/','-' ,'\' ,表示着一个数上的各位数字按相应字符上升,不变或下降,问【a,b】区间内这样的数有多少个?
分析;dp[i][j][k]第二维表示前一个值是什么,第三维表示当前处于波动字符串的第几个位置
要注意几点:
a、为了确保每个数只被计算一次,当能进入一个新的起伏时,尽量先进入,如果不能再判断是否符合之前的起伏。
b、前导零不应该被算入起伏中,起伏只能在没有前导零的数中匹配。
c、n个运算符至少要有n+1个数。
d、注意开始的状态,第一个数以及第一个运算符。
e、注意大数减一的计算。
f、注意减法的取模。
1 #include<cstdio>
2 #include<cstring>
3 #include<algorithm>
4 using namespace std;
5 typedef long long ll;
6 const int maxn=105;
7 const int mod=1e8;
8 char s1[maxn],s2[maxn],op[maxn];
9 int a[maxn];
10 int dp[maxn][12][maxn],len;
11
12 bool check ( int x , int y , char c )
13 {
14 if ( c == '/' && x < y ) return true ;
15 if ( c == '-' && x == y ) return true ;
16 if ( c == '\\' && x > y ) return true ;
17 return false ;
18 }
19
20 ll dfs(int pos,int pre,int p,bool lead,bool limit)
21 {
22 if ( pos==-1 ) return p==len;
23 if ( !limit && !lead && dp[pos][pre][p]!=-1 ) return dp[pos][pre][p];
24 int up=limit?a[pos]:9;
25 ll tmp=0;
26 for ( int i=0;i<=up;i++ )
27 {
28
29 if ( lead ) tmp=(tmp+dfs(pos-1,i,p,lead&&i==0,limit&&i==a[pos]))%mod;
30 else if ( p<len && check(pre,i,op[p+1]) ) tmp=(tmp+dfs(pos-1,i,p+1,lead,limit&&i==a[pos]))%mod;
31 else if ( p>0 && check(pre,i,op[p]) ) tmp=(tmp+dfs(pos-1,i,p,lead,limit&&i==a[pos]))%mod;
32 }
33 if ( !limit&&!lead ) dp[pos][pre][p]=tmp%mod;
34 return tmp;
35 }
36
37 ll solve(char *x,int f)
38 {
39 int i=0,pos=0;
40 int len_=strlen(x);
41 while ( x[i]=='0' ) i++;
42 if ( x[i]=='\0' ) return 0;
43 for( int j=len_-1;j>=i;j-- ) a[pos++]=x[j]-'0';
44 if( f )
45 {
46 a[0]--;
47 for( int j=0;j<pos;j++ )
48 {
49 if(a[j]<0)
50 {
51 a[j]+=10;
52 a[j+1]--;
53 }
54 }
55 }
56 pos--;
57 if( a[pos]==0 ) pos--;
58 return dfs(pos,0,0,true,true)%mod;
59 }
60
61 int main()
62 {
63 int l,r;
64 while ( scanf("%s",op+1)!=EOF )
65 {
66 memset(dp,-1,sizeof(dp));
67 len=strlen(op+1);
68 scanf("%s%s",s1,s2);
69 ll r=solve(s2,0);
70 ll l=solve(s1,1);
71 printf("%08lld\n",(r-l+mod)%mod);
72 }
73 return 0;
74 }
10.(HDOJ4352)http://acm.hdu.edu.cn/showproblem.php?pid=4352
题意:给定一个区间,让你求这个区间中数位从左到右满足LIS恰好为m的数的个数
分析:dp[i][j][k],第二维表示当前的状态(即LIS),第三维表示所求的LIS的长度
第二维中要用到LIS的nlogn的算法,具体见https://blog.csdn.net/shuangde800/article/details/7474903。 大致的含义就是j的二进制表示下有多少个位置为1代表LIS的长度为多少,从小到大出现的第i个1的位置j代表长度为i的LIS结尾为j。每次更新时,不断更新j(大致思想就是nlogn的LIS做法),注意记忆化返回的是要求的LIS长度的状态,而不是当前状态的LIS长度的状态
1 #include<cstdio>
2 #include<cstring>
3 #include<algorithm>
4 using namespace std;
5 typedef long long ll;
6 int a[25],m;
7 ll dp[25][1030][12];
8
9 int getnews(int x,int s)
10 {
11 for( int i=x;i<=9;i++ )
12 if(s&(1<<i)) return (s^(1<<i))|(1<<x);
13 return s|(1<<x);
14 }
15
16 int count(int x)
17 {
18 int cnt=0;
19 while ( x )
20 {
21 if ( x&1 ) cnt++;
22 x/=2;
23 }
24 return cnt;
25 }
26
27 ll dfs(int pos,int sta,bool lead,bool limit)
28 {
29 if ( pos==-1 ) return count(sta)==m;
30 if ( !limit && !lead && dp[pos][sta][m]!=-1 ) return dp[pos][sta][m];
31 int up=limit?a[pos]:9;
32 ll tmp=0;
33 for ( int i=0;i<=up;i++ )
34 {
35 tmp+=dfs(pos-1,(lead&&i==0)?0:getnews(i,sta),lead&&i==0,limit&&i==a[pos]);
36 }
37 if ( !limit&&!lead ) dp[pos][sta][m]=tmp;
38 return tmp;
39 }
40
41 ll solve(ll x)
42 {
43 int pos=0;
44 while ( x )
45 {
46 a[pos++]=x%10;
47 x/=10;
48 }
49 return dfs(pos-1,0,true,true);
50 }
51
52 int main()
53 {
54 ll l,r;
55 int T,h;
56 memset(dp,-1,sizeof(dp));
57 scanf("%d",&T);
58 for ( h=1;h<=T;h++ )
59 {
60 scanf("%lld%lld%d",&l,&r,&m);
61 printf("Case #%d: %lld\n",h,solve(r)-solve(l-1));
62 }
63 return 0;
64 }