zoukankan      html  css  js  c++  java
  • 数位DP

    讲解

    https://blog.csdn.net/brazy/article/details/77427699

    https://blog.csdn.net/wust_zzwh/article/details/52100392  

    数位dp是一种计数用的dp,一般就是要统计一个区间[le,ri]内满足一些条件数的个数。所谓数位dp,字面意思就是在数位上进行dp。

    数位的含义:一个数有个位、十位、百位、千位......数的每一位就是数位啦!

    之所以要引入数位的概念完全就是为了dp。数位dp的实质就是换一种暴力枚举的方式,使得新的枚举方式满足dp的性质,然后记忆化就可以了

    这些问题的特征是给定的区间特别大,不能一个个暴力的解决,必须用O(logN)的方法才行

    模板

    typedef long long ll;
    int a[20];
    ll dp[20][state];//不同题目状态不同
    ll dfs(int pos,/*state变量*/,bool lead/*前导零*/,bool limit/*数位上界变量*/)//不是每个题都要判断前导零
    {
        //递归边界,既然是按位枚举,最低位是0,那么pos==-1说明这个数我枚举完了
        if(pos==-1) return 1;/*这里一般返回1,表示你枚举的这个数是合法的,那么这里就需要你在枚举时必须每一位都要满足题目条件,也就是说当前枚举到pos位,一定要保证前面已经枚举的数位是合法的。不过具体题目不同或者写法不同的话不一定要返回1 */
        //第二个就是记忆化(在此前可能不同题目还能有一些剪枝)
        if(!limit && !lead && dp[pos][state]!=-1) return dp[pos][state];
        /*常规写法都是在没有限制的条件记忆化,这里与下面记录状态是对应,具体为什么是有条件的记忆化后面会讲*/
        int up=limit?a[pos]:9;//根据limit判断枚举的上界up;这个的例子前面用213讲过了
        ll ans=0;
        //开始计数
        for(int i=0;i<=up;i++)//枚举,然后把不同情况的个数加到ans就可以了
        {
            if() ...
            else if()...
            ans+=dfs(pos-1,/*状态转移*/,lead && i==0,limit && i==a[pos]) //最后两个变量传参都是这样写的
            /*这里还算比较灵活,不过做几个题就觉得这里也是套路了
            大概就是说,我当前数位枚举的数是i,然后根据题目的约束条件分类讨论
            去计算不同情况下的个数,还有要根据state变量来保证i的合法性,比如题目
            要求数位上不能有62连续出现,那么就是state就是要保存前一位pre,然后分类,
            前一位如果是6那么这意味就不能是2,这里一定要保存枚举的这个数是合法*/
        }
        //计算完,记录状态
        if(!limit && !lead) dp[pos][state]=ans;
        /*这里对应上面的记忆化,在一定条件下时记录,保证一致性,当然如果约束条件不需要考虑lead,这里就是lead就完全不用考虑了*/
        return ans;
    }
    ll solve(ll x)
    {
        int pos=0;
        while(x)//把数位都分解出来
        {
            a[pos++]=x%10;//个人老是喜欢编号为[0,pos),看不惯的就按自己习惯来,反正注意数位边界就行
            x/=10;
        }
        return dfs(pos-1/*从最高位开始枚举*/,/*一系列状态 */,true,true);//刚开始最高位都是有限制并且有前导零的,显然比最高位还要高的一位视为0嘛
    }
    int main()
    {
        ll le,ri;
        while(~scanf("%lld%lld",&le,&ri))
        {
            //初始化dp数组为-1,这里还有更加优美的优化,后面讲
            printf("%lld
    ",solve(ri)-solve(le-1));
        }
    }
    

    单纯的不要4,两种写法,递推、记忆化搜索(基本也是这两种方法)

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<stack>
    #include<cstdio>
    #include<queue>
    #include<map>
    #include<vector>
    #include<set>
    using namespace std;
    const int maxn=1010;
    const int INF=0x3fffffff;
    typedef long long LL;
    //两种方法 ,递推和记忆化搜索
    //单纯的不要4
    
    //递推
    int dp[20][10]; //表示第i位数,第1个数字是j时符合条件的数字数量
    int a[20];
    void inti(){   //初始化先 
    	dp[0][0]=1;
    	for(int i=1;i<=12;i++){
    		for(int j=0;j<10;j++){
    			for(int k=0;k<10;k++){
    				if(j!=4)
    				dp[i][j]+=dp[i-1][k];
    			}
    		}
    	}
    } 
    int solve1(int len){
    	int ans=0;
    	for(int i=len;i>=1;i--){  //从高位到低位处理
    		for(int j=0;j<a[i];j++)
    			if(j!=4){
    				ans+=dp[i][j];
    		if(a[i]==4) {
    			ans--;break;
    		}
    	}
    	return ans;
    }
    
    //记忆化搜索 
    int l,r,a[20];
    int dp[20];
    int dfs(int len,int ismax){
    	int ans=0;
    	int up;
    	if(!len) return 1;
    	if(!ismax&&dp[len]!=-1) return dp[len];
    	up=ismax? a[len]:9;
    	for(int i=0;i<=up;i++){
    		if(i==4) continue; 
    		ans+=dfs(len-1,ismax&&i==a[len]);
    	}
    	if(!ismax) dp[len]=ans;
    	return ans;
    }  
    int sovle2(int x){
    	int len=0;
    	memset(dp,-1,sizeof(dp));
    	while(x){
    		a[++len]=x%10;
    		x/=10;
    	}
    	return dfs(len,1)	
    }
    int main(){
    return 0;
    }
    

      

     

    1、2089 不要62

    数字不能出现4和62(连续的)

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<stack>
    #include<cstdio>
    #include<queue>
    #include<map>
    #include<vector>
    #include<set>
    using namespace std;
    const int maxn=1e7+10;
    const int INF=0x3fffffff;
    typedef long long LL;
    LL dp[maxn][2];  //后一维为0表示前面不是6,为1表示前面是6
    int l,r;
    int len[10];
    LL dfs(int pos,int pre,int state,int limit){  
    	if(pos==-1) return 1; //当已经到底了,就返回
    	if(!limit&&dp[pos][state]!=-1) return dp[pos][state];
    	int up=limit? len[pos]:9;
    	LL ans=0;
    	for(int i=0;i<=up;i++){  //下标从0开始 
    		if(i==4) continue;
    		if(pre==6&&i==2) continue;
    		ans+=dfs(pos-1,i,i==6?1:0,limit&&i==len[pos]);
    		//state 为 i 
    	}   
    	//保存结果 
    	if(!limit) dp[pos][state]=ans;
    	return ans; 
    } 
    LL solve(LL x){
    	int l=0;
    	while(x){
    		len[l++]=x%10;
    		x/=10;
    	}
    	return dfs(l-1,0,0,1);  //后面是1 
    }
    int main(){
    	while(scanf("%d %d",&l,&r)){
    		if(l==0&&r==0) break;
    		memset(dp,-1,sizeof(dp));
    		printf("%lld
    ",solve(r)-solve(l-1));
    	}
    return 0;
    }
    

    2、【HDU6148】Valley Number

    数字从左到右看不要有先递增后递减的情况、数位DP题的关键在于如何分析下一个数字的情况

    //这道题要考虑前导0的影响
    //而且对数字前后的要求
    int t;
    char s[110];
    int mod= 1000000007; 
    int a[110];
    LL dp[110][10][3];  //分别表示位数、前面的数pre,倾向turn
    LL dfs(int pos,int pre,int turn,bool limit,bool inv){ ///turn:0不清楚,1下降,2上升
    //这条语句中pos是要DP的位置,pre,turn,limit,invalid...这些都是前提条件,或者
    //说是之前位置上确定一些数之后的状态,而这个dfs要进行的就是在这个状态下继续
    //确定下一位的数字
    	if(pos==-1) return inv? 0:1;
    	if(!limit&&dp[pos][pre][turn]!=-1) return dp[pos][pre][turn];
    	int up=limit? a[pos]:9;
    	LL ans=0LL;  //注意要加LL
    	for(int i=0;i<=up;i++){
    		if(turn==2&&i<pre) continue; //不能先上升在下降
    		int p=0;
    		if(i==pre) p=turn;
    		else if(i<pre) p=1;
    		else p=2;
    		if(inv) p=0;  //随时控制前导0 
    		ans+=dfs(pos-1,i,p,limit&&i==a[pos],inv&&i==0);
    		ans%=mod;
    	} 
    	ans%=mod;
    	if(!limit) dp[pos][pre][turn]=ans;
    	return ans;
    } 
     
    int main(){
    	cin>>t;
    	while(t--){
    		scanf("%s",s);
    		memset(dp,-1,sizeof(dp)); 
    		int len=strlen(s);
    		for(int i=0;i<len;i++) a[i]=s[len-i-1]-'0';
    		printf("%lld
    ",dfs(len-1,0,0,1,1));
    	}
    return 0;
    }
    

    3、HDU 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的个数。

    常规想:这个f(x)计算就和数位计算是一样的,就是加了权值,所以dp[pos][sum],这状态是基本的。a是题目给定的,f(a)是变化的不过f(a)最大好像是4600的样子。如果要memset优化就要加一维存f(a)的不同取值,那就是dp[10][4600][4600],这显然不合法。

    这个时候就要用减法了,dp[pos][sum],sum不是存当前枚举的数的前缀和(加权的),而是枚举到当前pos位,后面还需要凑sum的权值和的个数,

    也就是说初始的是时候sum是f(a),枚举一位就减去这一位在计算f(i)的权值,那么最后枚举完所有位 sum>=0时就是满足的,后面的位数凑足sum位就可以了。
    仔细想想这个状态是与f(a)无关的(新手似乎很难理解),一个状态只有在sum>=0时才满足,如果我们按常规的思想求f(i)的话,那么最后sum>=f(a)才是满足的条件。

    但是在函数调用的时候

    intdfs(int pos,int sum,bool limit) 里面的sum表示的还是当前已经有的数

    //减法
    int f(int x){
    	if(x==0) return 0;
    	int ans=f(x/10);
    	return ans*2+(x%10);
    } 
    int dp[10][5000];
    int a[12];
    int all;
    int dfs(int pos,int sum,int limit){
    	if(pos==-1) return sum<=all; //注意这里 
    	if(sum>all) return 0; //如果大的话,就直接返回
    	if(!limit&&dp[pos][all-sum]!=-1) return dp[pos][all-sum];  //减法 
    	int up=limit? a[pos]:9;
    	int ans=0;
    	for(int i=0;i<=up;i++){
    		ans+=dfs(pos-1,sum+i*(1<<pos),limit&&i==a[pos]); //计算 
    	} 
    	if(!limit ) dp[pos][all-sum]=ans;
    	return ans;
    }
    int sovle(int x){
    	int len=0;
    	while(x){
    		a[len++]=x%10;
    		x/=10;
    	}
    	return dfs(len-1,0,1);
    }
    
    int main(){
    	int t,op=1;
    	int a,b;
    	scanf("%d",&t);
    	memset(dp,-1,sizeof(dp));
    	while(t--){
    		scanf("%d %d",&a,&b);
    		all=f(a);
    		//sovle(b);
    		printf("Case #%d: %d
    ",op++,sovle(b));
    	}
    return 0;
    }
    

    4、POJ 3252

    这题的约束就是一个数的二进制中0的数量要不能少于1的数量,通过上一题,这题状态就很简单了,dp[pos][num],到当前数位pos,0的数量减去1的数量不少于num的方案数,一个简单的问题,中间某个pos位上num可能为负数(这不一定是非法的,因为我还没枚举完嘛,只要最终的num>=0才能判合法,中途某个pos就不一定了),这里比较好处理,Hash嘛,最小就-32吧(好像),直接加上32,把32当0用。这题主要是要想讲一下lead的用法,显然我要统计0的数量,前导零是有影响的。至于!lead&&!limit才能dp,都是类似的,自己慢慢体会吧

    //要考虑前导零的影响,因为题目的要求
    //dp[pos][num],到当前数位pos,0的数量减去1的数量不少于num的方案数,一个简单的问题,
    //中间某个pos位上num可能为负数(这不一定是非法的,因为我还没枚举完嘛,只要最终的num>=0才能判合法,
    //中途某个pos就不一定了),这里比较好处理,Hash嘛,最小就-32吧(好像),直接加上32,把32当0用。这题主要是要想讲一下lead的用法,
    //显然我要统计0的数量,前导零是有影响的。!lead&&!limit才能dp
    int dp[35][100];
    int a[60];
    int dfs(int pos,int sta,bool lead,bool limit){  //位数、0-1的数,前导零,上限 
    	if(pos==-1) return sta>=32;
    	if(!lead&&!limit&&dp[pos][sta]!=-1) return dp[pos][sta];
    	int up=limit? a[pos]:1; //上限
    	int ans=0;
    	for(int i=0;i<=up;i++){
    		if(lead&&i==0) //如果有前导零,就略过
    		ans+=dfs(pos-1,sta,lead,limit&&i==a[pos]);
    		else ans+=dfs(pos-1,sta+(i==0? 1:-1),lead&&i==0,limit&&i==a[pos]); 
    	} 
    	if(!limit&&!lead) dp[pos][sta]=ans;
    	return ans;
    }
    int solve(LL x){
    	int len=0;
    	while(x){
    		a[len++]=x&1;
    		x>>=1;
    	}
    	return dfs(len-1,32,1,1); //以32为起点,以免中间有负数 
    }
    int main(){
    	memset(dp,-1,sizeof(dp));
    	LL a,b;
    	scanf("%lld %lld",&a,&b);
    	printf("%d
    ",solve(b)-solve(a-1));
    	
    	
    return 0;
    }
    

      

    一本通

    1585: 【例 1】Amount of Degrees

     跟上面的减法一样的思想,但是这个也有树的思想

    论文和题解:

    https://wenku.baidu.com/view/d2414ffe04a1b0717fd5dda8.html

    统计区间[0,x]内二进制表示含k个1的数的个数 。统计一棵高度为 i 的完全二叉树内二进制表示中恰好含有 j 个 1的数的个数 

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<stack>
    #include<cstdio>
    #include<queue>
    #include<map>
    #include<vector>
    #include<set>
    using namespace std;
    const int maxn=1010;
    const int INF=0x3fffffff;
    typedef long long LL;
    //这道题感觉理解还是不是很彻底
    //https://blog.csdn.net/primoblog/article/details/13168287?depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-3&utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-3
    int dp[32][32],a[32];
    int k,b,x,y; 
    void inti(){
    	dp[0][0]=1;
    	for(int i=1;i<=31;i++){
    		dp[i][0]=dp[i-1][0];  //有题解提到用完全二叉树理解
    		for(int j=1;j<=i;j++)
    		dp[i][j]=dp[i-1][j]+dp[i-1][j-1]; 
    	}
    }
    int calc(int xx){ ////统计区间[0,x]内二进制表示含k个1的数的个数 
    //统计一棵高度为 i 的完全二叉树内二进制表示中恰好含有 j 个 1的数的个数 
    	int tot=0,ans=0,len=0;//tot记录当前路径上已有的1的数量,ans为答案
    	while(xx){
    		a[++len]=xx%b;
    		xx/=b;
    	} 
    	for(int i=len;i>0;i--){
    		if(a[i]==1){
    			ans+=dp[i-1][k-(tot++)];
    			if(tot==k) break;
    		}
    		else if(a[i]>1){
    			ans+=dp[i][k-tot];
    			break;
    		}
    	}
    	return tot==k?ans+1:ans;
    }
    int main(){
    	scanf("%d %d %d %d",&x,&y,&k,&b);
    	inti();  //初始化 
    	printf("%d
    ",calc(y)-calc(x-1));
    return 0;
    }

    1586:【 例 2】数字游戏

    指定一个整数闭区间 [a,b],问这个区间内有多少个不降数。

    int f[32][32]/f[位数i][第i位(最高位)的数字]    的合理情况

    递推的做法:

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<stack>
    #include<cstdio>
    #include<queue>
    #include<map>
    #include<vector>
    #include<set>
    using namespace std;
    const int maxn=1010;
    const int INF=0x3fffffff;
    typedef long long LL;
    int f[32][32],a[32];
    // //f[位数i][第i位(最高位)的数字] 
    void inti(){ //初始化 
    	for(int i=1;i<=31;i++) f[1][i]=1;
    	for(int i=2;i<=31;i++){
    		for(int j=0;j<=9;j++){
    			for(int k=j;k<=9;k++){
    				f[i][j]+=f[i-1][k];
    			}
    		}
    	}
    } 
    int solve(int x){
    	int len=0;
    	memset(a,0,sizeof(a));
    	while(x){
    		a[++len]=x%10;
    		x/=10;
    	}
    	int ans=0;  //方案数
    	for(int i=len;i;i--){
    		if(a[i+1]>a[i]) break;  //不降数,下降了
    		for(int j=a[i+1];j<a[i];j++){  //不降数,必须比前一个大,比现在这个小 
    			ans+=f[i][j];
    		}
    		if(i==1) ans++; //本身也是 
    	} 
    	return ans;
    }
    int main(){
    	int x,y;
    	inti();
    	while(scanf("%d %d",&x,&y)!=EOF){
    		printf("%d
    ",solve(y)-solve(x-1));
    	}
    	 
    return 0;
    }

    记忆化搜索的做法:

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<stack>
    #include<cstdio>
    #include<queue>
    #include<map>
    #include<vector>
    #include<set>
    using namespace std;
    const int maxn=1010;
    const int INF=0x3fffffff;
    typedef long long LL;
    //不降数这道题的记忆化搜索
    int bit[15];
    int dp[15][10];
    int l,r,len;
    int dfs(int pos,int pre,int limit){
    	if(pos>len) return 1;
    	if(!limit&&dp[pos][pre]!=-1) return dp[pos][pre];
    	int up=limit? bit[len-pos+1]:9;
    	int ans=0;
    	for(int i=pre;i<=up;i++){
    		ans+=dfs(pos+1,i,limit&&i==up);
    	}
    	if(!limit) dp[pos][pre]=ans;
    	return ans;
    } 
    int chan(int x){
    	len=0;
    	while(x){
    		bit[++len]=x%10;
    		x/=10;
    	}
    	memset(dp,-1,sizeof(dp));
    	return dfs(1,0,1);
    }
    int main(){
    	while(~scanf("%lld %lld",&l,&r)){
    		printf("%lld
    ",chan(r)-chan(l-1));
    	}
    return 0;
    }
    
    

    1587: 【例 3】Windy 数

    Windy 定义了一种 Windy 数:不含前导零且相邻两个数字之差至少为2的正整数被称为 Windy 数。

    递推的做法(感觉要好理解一点)TAT

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<stack>
    #include<cstdio>
    #include<queue>
    #include<map>
    #include<vector>
    #include<set>
    using namespace std;
    const int maxn=1010;
    const int INF=0x3fffffff;
    typedef long long LL;
    //看了一下,觉得这道题用递推更好理解
    int f[32][32],a[32];
    // //f[位数i][第i位(最高位)的数字] 
    void inti(){   //递推都需要初始化 
    	for(int i=0;i<=9;i++) f[1][i]=1; //只有1位 
    	for(int i=2;i<=31;i++){
    		for(int j=0;j<=9;j++){
    			for(int k=0;k<=9;k++){
    				if(abs(j-k)>=2) f[i][j]+=f[i-1][k];
    			}
    		}
    	}
    }
    int solve(int x){
    	memset(a,0,sizeof(a));
    	int len=0;
    	while(x){
    		a[++len]=x%10;
    	//	cout<<a[len]<<" ";
    		x/=10;
    	}
    //	cout<<endl;
    	//按照顺序处理位数
    	int ans=0;
    	for(int i=1;i<len;i++){ //不足len的位的部分 
    		for(int j=1;j<=9;j++)  ans+=f[i][j];
    		//不能从0开始 
    	} 
    	for(int i=1;i<a[len];i++) //第len位不足a[len]的部分
    	ans+=f[len][i];
    	//然后处理第len位为a[len]的数据,因为是很大的树,所以要控制上限
    	for(int i=len-1;i;i--){
    		for(int j=0;j<a[i];j++){ //最高位已经确定了,所以可以取到0了 
    			if(abs(j-a[i+1])>=2) ans+=f[i][j]; ////跟前一位比较
    		}
    		if(abs(a[i+1]-a[i])<2) break; 
    		if(i==1) ans++; //?因为上面处理 不足len的位的部分 的时候没有处理1位 
    	}
    	 return ans; 
    }
    
    int main(){
    	inti();
    	int x,y;
    	cin>>x>>y;
    	cout<<solve(y)-solve(x-1)<<endl;
    return 0;
    }
    

    记忆化搜索  dp[i][j]第i高位,j填的数字

    //记忆化搜索
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    int f[10][10],a[10];//f[第i高位][填的数字]
    int dfs(int len,int st,int ze,int lim)//ze是否有前导零  lim:是否达到上限 
    {
    	if (len==0) return 1;
    	if (lim==0&&f[len][st]!=-1) return f[len][st];
    	int ans=0,ed; 
    	if (lim==1) ed=a[len]; else ed=9; 
    	for (int i=0;i<=ed;i++) 
    	{ 
    		if (ze==1)
    		{
    			int zz=0;
    			if (i==0) zz=1; 
    			if (lim==1&&i==ed) ans+=dfs(len-1,i,zz,1);
    			else ans+=dfs(len-1,i,zz,0);
    		}
    		else if (abs(st-i)>=2)
    		{
    			if (lim==1&&i==ed) ans+=dfs(len-1,i,0,1);
    			else ans+=dfs(len-1,i,0,0);
    		}
    	}
    	if (lim==0&&st!=0) f[len][st]=ans;
    	return ans;
    }
    int solve(int x)
    {
    	memset(a,0,sizeof(a));
    	int len=0;
    	while (x)
    	{
    		a[++len]=x%10;
    		x/=10;
    	}
    	return dfs(len,0,1,1);
    }
    int main()
    {
    	memset(f,-1,sizeof(f));
    	int a,b;
    	scanf("%d%d",&a,&b);
    	printf("%d
    ",solve(b)-solve(a-1));
    	return 0;
    }
    

    1588:数字游戏

    某人又命名了一种取模数,这种数字必须满足各位数字之和mod N=0 。现在大家又要玩游戏了,指定一个整数闭区间[a,b],问这个区间内有多少个取模数。

    这道题还比较简单,但是要注意函数写法,返回的是summ==0;  //返回一个判断

    ans+=dfs(pos+1,(summ+i)%k,limit&&i==up);

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<stack>
    #include<cstdio>
    #include<queue>
    #include<map>
    #include<vector>
    #include<set>
    using namespace std;
    const int maxn=1010;
    const int INF=0x3fffffff;
    typedef long long LL;
    int l,r,k;
    int dp[40][110];
    int num[40];
    int len; 
    int dfs(int pos,int summ,int limit){
    	if(pos>len) return summ==0;  //返回一个判断
    	if(dp[pos][summ]!=-1&&!limit) return dp[pos][summ]; 
    	int up=limit?num[len-pos+1]:9;
    	int ans=0;
    	for(int i=0;i<=up;i++){
    		ans+=dfs(pos+1,(summ+i)%k,limit&&i==up);
    	}
    	if(!limit) dp[pos][summ]=ans;
    	return ans;
    }
    int solve(int x){
    	memset(num,0,sizeof(num));
    	memset(dp,-1,sizeof(dp));
    	len=0;
    	while(x){
    		num[++len]=x%10;
    		x/=10;
    	}
    	return dfs(1,0,1);
    }
    int main(){
    	
    	while(~scanf("%d %d %d",&l,&r,&k)){
    		printf("%d
    ",solve(r)-solve(l-1));
    	}
    	
    return 0;
    }

    1590:恨 7 不成妻

    好难TAT  https://blog.csdn.net/deerly_/article/details/79930085

    https://blog.csdn.net/ten_three/article/details/19698055

    需要维护三个值   --->定义结构体,假定dfs推出返回的结构体是tmp,当前结果的结构体是ans 

    1.符合条件数的个数 cnt 

    2.符合条件数的和 sum 

    3.符合条件数的平方和 sqr 

    三个条件

    (1)数中某一位是 7;   基础的数位dp很好维护;

    (2)整数的每一位加起来的和是7 的整数倍;

    tmp.sum * 10 + (10 ^ pos * i) * ans.cnt 就是上一步状态的和加上这一步加的 

    这一步加的就是10的当前位次方乘以i,因为有ans.cnt个嘛,所以再乘以ans.cnt 

    (3)这个整数是 7 的整数倍。

    3 首先重新构建一下这个数 (10^pos * i + x)x是这个数的后面部分,就是上一次状态得到的那个数,则平方和就是(10^len*i)^2+x^2+2*10^len*i*x, 其中x^2=tmp.sqr; 

    ans.sqr += (2*10^pos*i*x)*tmp.cnt=(2*10^pos*i)*next.sum(神奇的化简) 

    ans.sqr += (10^pos*i)^2*tmp.cnt;

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<stack>
    #include<cstdio>
    #include<queue>
    #include<map>
    #include<vector>
    #include<set>
    using namespace std;
    const int maxn=1010;
    const int INF=0x3fffffff;
    const long long MOD = 1e9 + 7;
    typedef long long LL;
    //好难啊
    /*
    整数中某一位是 77;
    整数的每一位加起来的和是 77 的整数倍;
    这个整数是 77 的整数倍。
    首先这三个条件都是基础数位dp,就不说了。 
    重点是怎么去求平方和。 
    需要维护三个值 
    1.符合条件数的个数 cnt 
    2.符合条件数的和 sum 
    3.符合条件数的平方和 sqr 
    为什么要维护这三个呢,接着往下看你就知道了 
    假定dfs推出返回的结构体是tmp,当前结果的结构体是ans 
    其中1是基础的数位dp很好维护; 
    2 tmp.sum * 10 + (10 ^ pos * i) * ans.cnt 就是上一步状态的和加上这一步加的 
    这一步加的就是10的当前位次方乘以i,因为有ans.cnt个嘛,所以再乘以ans.cnt 
    3 首先重新构建一下这个数 (10^pos * i + x)x是这个数的后面部分,就是上一次状态得到的那个数,则平方和就是(10^len*i)^2+x^2+2*10^len*i*x, 其中x^2=tmp.sqr; 
    ans.sqr += (2*10^pos*i*x)*tmp.cnt=(2*10^pos*i)*next.sum(神奇的化简) 
    ans.sqr += (10^pos*i)^2*tmp.cnt;
    原文链接:https://blog.csdn.net/deerly_/article/details/79930085
    */ 
    LL p[25];  //这个是位数,1 10 100 1000 10000这种
    LL num[40];
    struct node{  //要用到结构体 
    	LL cnt,summ,sqr;   
    	//符合条件数的个数    符合条件数的和    符合条件数的平方和
    	node(){cnt=-1,summ=sqr=0;}
    	node(LL cnt,LL summ,LL sqr)  : cnt(cnt),summ(summ),sqr(sqr){}
    }dp[20][20][20]; 
    LL t,l,r,len;
    node dfs(int pos,int sum1,int sum2,bool limit){  //sum1为每一位加起来的和,sum2 
    	if(pos==0){
    		if(sum1&&sum2){
    			return node(1,0,0); //个数为1 
    		}
    		 return node(0,0,0);
    	}
    	if(!limit&&dp[pos][sum1][sum2].cnt!=-1) return dp[pos][sum1][sum2];
    	int up=limit? num[pos]:9;
    	node ans;
    	ans.cnt=0;
    	for(int i=0;i<=up;i++){
    		if(i==7) continue;
    		node temp=dfs(pos-1,(i+sum1)%7,(sum2*10+i)%7,limit&&i==up);
    		ans.cnt+=temp.cnt; //这个可以直接加,很好维护 
    		ans.cnt%=MOD;
    		ans.summ+=(temp.summ+((p[pos]*i)%MOD)*temp.cnt%MOD)%MOD;
    		ans.summ%MOD;
    		//看看这个怎么计算的 
    		ans.sqr+=(temp.sqr+((2*p[pos]*i)%MOD)*temp.summ)%MOD;
    		ans.sqr%=MOD;
    		ans.sqr+=((temp.cnt*p[pos])%MOD*p[pos]%MOD*i*i%MOD);
    		ans.sqr%=MOD;
    	}
    	if(!limit) dp[pos][sum1][sum2]=ans;
    	return ans; 
    }
    LL solve(LL n){
    	len=0;
    	while(n){
    		num[++len]=n%10;
    		n/=10;
    	}
    	node v=dfs(len,0,0,1);
    	return v.sqr;
    }
    
    
    int main(){
    	scanf("%d",&t);
    	p[1]=1;
    	for(int i=2;i<=20;i++) p[i]=(p[i-1]*10)%MOD;
    	while(t--){
    		scanf("%lld %lld",&l,&r);
    		LL ans=solve(r);
    		ans-=solve(l-1);
    		printf("%lld
    ",(ans%MOD+MOD)%MOD);
    	}
    return 0;
    }

    这个也很好理解

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    using namespace std;
    #define LL long long
    #define mod 1000000007
    struct node
    {
    	LL s,sum,sqrsum;//个数N 各数之和 各数平方和 
    	node()
    	{
    		s=sum=sqrsum=-1;
    	}
    }f[20][10][10];//f[位数][各数位之和mod7][这个数mod7] 
    int a[20];
    LL pi[20];
    node dfs(int len,int s,int qs,int lim)//数位 各数位之和mod7 这个数mod7 是否上限 
    {
    	if (len==0)
    	{
    		node tt;
    		tt.s=tt.sum=tt.sqrsum=0;
    		if (s!=0&&qs!=0) tt.s=1;
    		return tt;
    	}
    	if (lim==0&&f[len][s][qs].sqrsum!=-1) return f[len][s][qs];
    	node ans; ans.s=ans.sum=ans.sqrsum=0;
    	int ed=0;
    	if (lim==1) ed=a[len]; else ed=9;
    	for (int i=0;i<=ed;i++)
    	{
    		if (i==7) continue;
    		/*
    		第len位填i f1,f2,f2...fN记录前len位已经填好的数字
    		对答案的贡献为:  (f1+ i*10^(len-1) )^2+(f2+ i*10^(len-1) )^2+...+(fN+ i*10^(len-1) )^2
    		将平方和展开 整理 提公因式:
    		N*(i*10^(len-1))^2+ f1^2+f2^2+...+fN^2+ 2*(i*10^(len-1))*(f1+f2+...fN) 
    		其中 N即为要求的s   2*(i*10^(len-1))*(f1+f2+...fN) 即为要求的sum   整一个即为sqrsum 
    		*/
    		node tt;
    		if (lim==1&&i==ed) tt=dfs(len-1,(s+i)%7,(qs*10+i)%7,1);
    		else tt=dfs(len-1,(s+i)%7,(qs*10+i)%7,0);
    		LL si=(i*pi[len-1])%mod;
    		ans.s=(ans.s+tt.s)%mod; 
    		ans.sum=(ans.sum+(tt.sum+ (si*tt.s)%mod ) %mod )%mod;
    		ans.sqrsum=(ans.sqrsum+tt.sqrsum+( ((si*si)%mod*tt.s)%mod+ ((2*si)%mod *tt.sum) %mod )%mod )%mod;
    	}
    	if (lim==0) f[len][s][qs]=ans;
    	return ans;
    }
    LL solve(LL x)
    {
    	memset(a,0,sizeof(a));
    	int len=0;
    	while (x)
    	{
    		a[++len]=x%10;
    		x/=10;
    	}
    	node ans=dfs(len,0,0,1);
    	return ans.sqrsum%mod;
    }
    int main()
    {
    	pi[0]=1;
    	for (int i=1;i<=18;i++) pi[i]=pi[i-1]*10;
    	int t;
    	scanf("%d",&t);
    	while (t--)
    	{
    		LL x,y;
    		scanf("%lld%lld",&x,&y);
    		if (x>y) swap(x,y);
    		printf("%lld
    ",(solve(y)-solve(x-1)+mod)%mod);
    	}
    	return 0;
    }
    

      

    1591:数字计数

    给定两个正整数a和b,求在[a,b]中的所有整数中,每个数码digit 各出现了多少次。

    10次dfs

    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<algorithm>
    #include<stack>
    #include<cstdio>
    #include<queue>
    #include<map>
    #include<vector>
    #include<set>
    using namespace std;
    const int maxn=1010;
    const int INF=0x3fffffff;
    typedef long long LL;
    LL l,r;
    LL len;
    LL dp[20][110];
    LL num[20];
    LL dfs(int pos,int dig,LL summ,int lead,int limit){
    	//位数   上一个数字   方案数  前导0   上界
    	if(pos>len) return summ;
    	if(dp[pos][summ]!=-1&&!limit&&!lead) return dp[pos][summ];
    	int up=limit? num[len-pos+1]:9;  //是递增的 
    	LL ans=0;
    	for(int i=0;i<=up;i++){
    		if(lead&&i==0) //有前导零 
    		ans+=dfs(pos+1,dig,0,1,up==i&&limit); //方案数位0 
    		else
    		ans+=dfs(pos+1,dig,summ+(i==dig?1:0),0,i==up&&limit);  //是对应的数字的话,那么方案数+1 
    	}
    	if(!limit&&!lead) dp[pos][summ]=ans;
    	return ans;
    }
    LL work(LL x,int dig){																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																																				
    	memset(dp,-1,sizeof(dp));
    	len=0;
    	while(x){
    		num[++len]=x%10;
    		x/=10;
    	}
    	return dfs(1,dig,0,1,1);
    }
    int main(){
    	scanf("%lld %lld",&l,&r);
    	if(l){
    		for(int i=0;i<=9;i++){
    			printf("%lld ",work(r,i)-work(l-1,i));
    		}
    	}
    	else{
    		for(int i=0;i<=9;i++){
    			printf("%lld ",work(r,i)-work(l,i));
    		}
    	}
    return 0;
    }
    
  • 相关阅读:
    红帽7 创建网络会话
    红帽7 Iptables与Firewalld防火墙
    红帽7 配置网卡
    红帽7 LVM逻辑卷管理器
    红帽7 RAID磁盘冗余阵列
    红帽7 磁盘划分
    wpf学习一(转)
    选中当前点击的位置
    c#客显
    两个程序间的通信有三种
  • 原文地址:https://www.cnblogs.com/shirlybaby/p/12318799.html
Copyright © 2011-2022 走看看