2019华东双基(I题数位dp,)

I题-不要666

链接:https://ac.nowcoder.com/acm/contest/1168/I

满足以下3个条件中的一个,我们就认为这个整数与6有关。
1.这个整数在10进制下某一位是6.
2.这个整数在10进制下的数位和是6的倍数.
3.这个数是6的整数倍。

输入描述:
本题为多组输入,请处理到文件结尾,每行包含两个正整数L,R。(1<=L<=R<=1e18)。
输出描述:
输出一个正整数,该正整数为区间【L,R】中与6无关的数字的和。由于这个数字可能很大,请对1e9+7取模。


①比赛时写这题时,用的是二维dp,结果只过了样例;
②比赛结束后,看了下别人的代码,用的是3维dp,而且计算答案是直接计算与6无关的数的和。对照后将dp改成三维,结果还是只过了样例。
多了一维的变量是num,计算过程中对num取余6,因为num是用来考虑这个数是否是6的倍数;
而赛时自己写的计算结果公式 :(L+R)*(R-L+1)/2 - 与6有关数的总和
然后利用数位dp求 :与6有关数的总和
两种方法就得注意一些不同的地方,因为如果计算与6有关的数的和就必须考虑题目中的三种情况。而计算与6无关的总和就不用考虑了,三维dp中的pos,num,sum三个变量就可以存;
后面想了下三维dp中多的一维num的作用,因为如果直接用两维pos,sum的话,dp里存的值包含的情况是有点多了,如果dfs中要用到dp的以计算好的结果,就会将不符合条件的情况包含进去。
维num:用来存数取余6的结果,对于题目中的条件3
维sum:数位之和,对应题目中的条件2
因此对于计算与6有关的数的总和,dp存的结果里不包含满足条件1的数,可以直接dfs的for循环中进行判断直接continue,三维pos,num,sum就够了
而对于计算与6无关的数的总和,dp存的必须得有满足条件1的数,这样三维就不行了,得再增加一维isix,判断是否含数字6;
后面是这种做法的代码

对于维度到底得开几维的问题,在做完这题后的一些理解(口糊一下):
dp中存的结果不会与dfs()里边的参数所代表的含义相冲突
解释下就是得把dp中不该合并的结果,通过增加维度分别存起来,这样在使用的时候就不会有冲突
如果参数定义没问题的话,dp的维度可以参考参数

代码:

#include 
#define ll long long
using namespace std;
const int mod= 1e9+7;
 
struct node{
	ll a,b;
	node(ll _a, ll _b):a(_a),b(_b){}
	node():a(-1),b(0){}
}dp[20][10][3][180];// pos - num - isix-  sum  : 位置- 数取余6余的数 - 是否含数字6 - 数位和  
int digit[20];
 
node dfs(int pos,int num,int sum, int isix,int limit) //num 用来标记pos位余num的
{
    if(pos==-1)
	{
		if(isix||num%6==0||sum%6==0) 
			// return  node{1,0}; 如果加了结构体有node(),就不能用node{1,0}
			return node(1,0);
		return node(0,0);
	}
    if(!limit&&dp[pos][num][isix][sum].a!=-1) return dp[pos][num][isix][sum];
    int up = limit ? digit[pos] : 9;
    node ans,tmp;
	ans.a=0;ans.b=0;
    for(int i = 0; i <= up; i++)
	{
		
		tmp=dfs(pos-1,(num*10+i)%6,(sum + i) , isix||(i==6),limit && i==digit[pos]);
    
		tmp.a%=mod;tmp.b%=mod;
		ans.a=(ans.a+tmp.a)%mod;
		ll y=pow(10,pos);
		ans.b=(ans.b+tmp.b+1LL*y%mod*i%mod*tmp.a%mod)%mod;
		
	}
	if(!limit) dp[pos][num][isix][sum] = ans;
	
    return ans;
}
 
ll solve(ll x)
{
    if(x <0) return 0;
    int len = 0;
    while(x)
    {
        digit[len++] = x%10;
        x/=10;
    }
	return dfs(len-1,0,0,0,1).b;
}
 
int main()
{
    int t;
    ll a, b;
    
    
       while(~scanf("%lld%lld", &a, &b)){
        cout << ((1LL*(b-a+1)%mod*((a+b)%mod)%mod*((mod+1)/2)%mod-(solve( b) -solve(a-1)))%mod+mod)%mod << endl;
	   }
    return 0;
}

你可能感兴趣的:(数位dp,学校比赛)