1590 恨 7 不成妻(HDU4507 LOJ10168) 暴力0分 能处理平方和数位DP(包含数本身 数位和 两种状态) 到处要模否则溢出

总目录

在线测评地址(ybt)

在线测评地址(LOJ)

1.暴力0分

ybt

未通过

测试点结果内存时间
测试点1运行超时576KB997MS
测试点2运行超时592KB998MS

LOJ

 暴力0分代码如下:

#include <bits/stdc++.h>
#define LL long long
#define mod 1000000007
using namespace std;
int main(){
	int t,flag,sum;
	LL lt,rt,i,x,ans;
	scanf("%d",&t);
	while(t--){
		scanf("%lld%lld",&lt,&rt);
		ans=0;
		for(i=lt;i<=rt;i++){
			x=i;
			if(x%7==0)continue;
			flag=0,sum=0;
			while(x){
				if(x%10==7){
					flag=1;
					break;
				}else sum+=x%10;
				x/=10;
			}
			if(flag)continue;
			if(sum%7==0)continue;
			ans=(ans+i*i%mod)%mod;
		}
		printf("%lld\n",ans);
	}
	return 0;
} 

2.数位DP

(1)整数中某一位是 7;(数位DP很容易编写)

(2)整数的每一位加起来的和是 7的整数倍;(作为中间值进行传递,数位DP也可编写)

(3)这个整数是 7的整数倍。(作为中间值进行传递,数位DP也可编写,不过编写没把握)

困难:将(2)(3)综合起来比较困难,还有dp数组应该如何设计.dp[pos][]另一个维度应该如何设计?

我们先定义结构体,其中存储三个变量。

  1. 个数 cnt

  2. 和 sum

  3. 平方和 sqsum

定义dp[i][j][k] 表示前i位数,各位数之和mod7(j),这个数mod7中还能合法的数的个数(k)。

然后涮刷刷一边数位dp dfs的模板

之后就到了最难的一点,如何统计平方和

以两位数2X为例:
初始化
对应的个数 cnt=0
对应的和 sum=0
对应的平方和 sqsum=0

第0位符合题意的数据是1,2,3,4,5,6,8,9
对应的个数 cnt=8
对应的和 sum=1+2+3+4+5+6+8+9=38
对应的平方和 sqsum=1*1+2*2+3*3+4*4+5*5+6*6+8*8+9*9=236

到达第1位0的计算,符合题意的数据是01,02,03,04,05,06,08,09
对应的个数 cnt=8
对应的和 sum=1+2+3+4+5+6+8+9=38
对应的平方和 sqsum=1*1+2*2+3*3+4*4+5*5+6*6+8*8+9*9=236

到达第1位1的计算,符合题意的数据是01,02,03,04,05,06,08,09,10,11,12,13,15,18,19
对应的个数 cnt=15
对应的和 sum=1+2+3+4+5+6+8+9+10+11+12+13+15+18+19=136
对应的平方和 sqsum=1*1+2*2+3*3+4*4+5*5+6*6+8*8+9*9+10*10+11*11+12*12+13*13+15*15+18*18+19*19
=1680

到达第1位2的计算,符合题意的数据是01,02,03,04,05,06,08,09,10,11,12,13,15,18,19,20,22,23,24,26,29
对应的个数 cnt=21
对应的和 sum=1+2+3+4+5+6+8+9+10+11+12+13+15+18+19+20+22+23+24+26+29=280
对应的平方和 sqsum=1*1+2*2+3*3+4*4+5*5+6*6+8*8+9*9+10*10+11*11+12*12+13*13+15*15+18*18+19*19+20*20+22*22+23*23+24*24+26*26+29*29=5186

我们的转移是这样滴

首先关于个数的转移是就直接把后面的叠加不就完了,标准转移。

以两位数2X为例:
初始化
对应的个数 cnt=0


第0位符合题意的数据是1,2,3,4,5,6,8,9
对应的个数 cnt=8


到达第1位0的计算,符合题意的数据是01,02,03,04,05,06,08,09
对应的个数 cnt=8


到达第1位1的计算,符合题意的数据是01,02,03,04,05,06,08,09,10,11,12,13,15,18,19
01,02,03,04,05,06,08,09有8个数
10,11,12,13,15,18,19有7个数
对应的个数 cnt=8+7=15


到达第1位2的计算,符合题意的数据是01,02,03,04,05,06,08,09,10,11,12,13,15,18,19,20,22,23,24,26,29
01,02,03,04,05,06,08,09,10,11,12,13,15,18,19有15个数
20,22,23,24,26,29有6个数
对应的个数 cnt=15+6=21

和的转移则是稍微动下脑子就可以轻松写出来,sum_now += cnt_son*(10^pos*i)。

以两位数2X为例:
初始化
对应的个数 cnt=0
对应的和 sum=0


第0位符合题意的数据是1,2,3,4,5,6,8,9
对应的个数 cnt=8
对应的和 sum=1+2+3+4+5+6+8+9=38


到达第1位0的计算,符合题意的数据是01,02,03,04,05,06,08,09
对应的个数 cnt=8
对应的和 sum=1+2+3+4+5+6+8+9=38


到达第1位1的计算,符合题意的数据是01,02,03,04,05,06,08,09,10,11,12,13,15,18,19
对应的个数 cnt=8+7=15
对应的和 
sum=1+2+3+4+5+6+8+9+10+11+12+13+15+18+19
=38+(10+11+12+13+15+18+19)
=38+(10+10+1+10+2+10+3+10+5+10+8+10+9)
=38+(10*7+1+2+3+5+8+9)
=38+(10*7+28)
=136


到达第1位2的计算,符合题意的数据是01,02,03,04,05,06,08,09,10,11,12,13,15,18,19,20,22,23,24,26,29
对应的个数 cnt=15+6=21
对应的和 
sum=1+2+3+4+5+6+8+9+10+11+12+13+15+18+19+20+22+23+24+26+29
=136+(20+22+23+24+26+29)
=136+(20*6+2+3+4+6+9)
=136+(20*6+24)
=280

当前位数对于和的贡献是存在于之后的都合法,那么这一位对于和的叠加就应该是它本身的值乘上之后合法的数目。(就是有多少加多少。。实在看不懂琢磨琢磨代码应该就懂了)在基于这两点后,我们对于sqsum的求解就可以借由一个初中大家都学过的公式了

(a+b)^2 = a^2+2ab+b^2;

显然之前的sqsum已经将之前所有数的平方和都求完了,实际上我们求出的是b1^2+b2^2+b3^2……bn^2。

那么我们发现在这一位的a就是i*10^pos,并且对于每一个b都相同,所以转移可以这么写。

sqsum_now = sqsum_son + 2*sum_son*(10^pos*i) +(10^pos*i)*(10^pos*i)*cnt_son;

以两位数2X为例:
初始化
对应的个数 cnt=0
对应的和 sum=0
对应的平方和 sqsum=0

第0位符合题意的数据是1,2,3,4,5,6,8,9
对应的个数 cnt=8
对应的和 sum=38
对应的平方和 sqsum=1*1+2*2+3*3+4*4+5*5+6*6+8*8+9*9=236

到达第1位0的计算,符合题意的数据是01,02,03,04,05,06,08,09
对应的个数 cnt=8
对应的和 sum=38
对应的平方和 sqsum=1*1+2*2+3*3+4*4+5*5+6*6+8*8+9*9=236

到达第1位1的计算,符合题意的数据是01,02,03,04,05,06,08,09,10,11,12,13,15,18,19
对应的个数 cnt=8+7=15
对应的和 sum==38+(10*7+1+2+3+5+8+9)
=38+(10*7+28)
=136
对应的平方和 sqsum=1*1+2*2+3*3+4*4+5*5+6*6+8*8+9*9+10*10+11*11+12*12+13*13+15*15+18*18+19*19
=236+(10*10+11*11+12*12+13*13+15*15+18*18+19*19)
=236+(10*10+(10+1)*(10+1)+(10+2)*(10+2)+(10+3)*(10+3)+(10+5)*(10+5)+(10+8)*(10+8)+(10+9)*(10+9))
=236+(10*10*7+2*10*(1+2+3+5+8+9)+(1*1+2*2+3*3+5*5+8*8+9*9))
=236+(10*10*7+2*10*28+184)
=1680

到达第1位2的计算,符合题意的数据是01,02,03,04,05,06,08,09,10,11,12,13,15,18,19,20,22,23,24,26,29
对应的个数 cnt=15+6=21
对应的和 sum=136+(20*6+2+3+4+6+9)
=136+(20*6+24)
=280

对应的平方和 sqsum=1*1+2*2+3*3+4*4+5*5+6*6+8*8+9*9+10*10+11*11+12*12+13*13+15*15+18*18+19*19
=1680+(20*20+22*22+23*23+24*24+26*26+29*29)
=1680+(20*20+(20+2)*(20+2)+(20+3)*(20+3)+(20+4)*(20+4)+(20+6)*(20+6)+(20+9)*(20+9))
=1680+(20*20*6+2*20*(2+3+4+6+9)+(2*2+3*3+4*4+6*6+9*9))
=1680+(20*20*6+2*20*24+146)
=5186

至此,所有状态转移完毕.

编码过程中,还有一个难点,到处要模否则溢出

ybt

通过

测试点结果内存时间
测试点1答案正确660KB5MS
测试点2答案正确652KB7MS

LOJ

 能处理平方和数位DP代码如下:

#include <bits/stdc++.h>
#define LL long long
#define mod 1000000007
using namespace std;
struct node{
	LL cnt,sum,sqsum;//数量,和,平方和 
}dp[20][10][10];//dp[pos][num%7][sum%7]//num数本身,sum数位和 
int bit[20];
LL p[20];
node dfs(int pos,int num,int sum,int limit){
	node ans,tmp;
	int i,up;
	if(pos==-1){
		tmp.cnt=(num!=0&&sum!=0);
		tmp.sum=0;
		tmp.sqsum=0;
		return tmp;
	}
	if(!limit&&dp[pos][num][sum].cnt!=-1)return dp[pos][num][sum];
	up=limit?bit[pos]:9;
	ans.cnt=ans.sum=ans.sqsum=0;
	for(i=0;i<=up;i++){
		if(i==7)continue;
		tmp=dfs(pos-1,(num*10+i)%7,(sum+i)%7,limit&&i==up);
		ans.cnt+=tmp.cnt;
		ans.cnt%=mod;
		ans.sum+=((p[pos]*i)%mod*tmp.cnt%mod+tmp.sum)%mod;
		ans.sum%=mod;
		ans.sqsum+=(p[pos]*i)%mod*(p[pos]*i)%mod*tmp.cnt%mod;
		ans.sqsum+=(2*(p[pos]*i)%mod*tmp.sum%mod+tmp.sqsum)%mod;
		ans.sqsum%=mod;
	}
	if(!limit)dp[pos][num][sum]=ans;
	return ans;
}
LL solve(LL x){
	int pos=0;
	while(x){
		bit[pos++]=x%10;
		x/=10;
	}
	return dfs(pos-1,0,0,1).sqsum;
}
int main(){
	int T,i;
	LL lt,rt;
	scanf("%d",&T);
	p[0]=1;
	for(i=1;i<=18;i++)p[i]=p[i-1]*10%mod;
	while(T--){
		LL ret;
		scanf("%lld%lld",&lt,&rt);
		memset(dp,-1,sizeof(dp));
		ret=((solve(rt)-solve(lt-1))%mod+mod)%mod;//避免出现负数的常见手法 
		printf("%lld\n",ret);
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值