1.暴力0分
ybt
未通过
测试点 | 结果 | 内存 | 时间 |
测试点1 | 运行超时 | 576KB | 997MS |
测试点2 | 运行超时 | 592KB | 998MS |
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",<,&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][]另一个维度应该如何设计?
我们先定义结构体,其中存储三个变量。
-
个数 cnt
-
和 sum
-
平方和 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 | 答案正确 | 660KB | 5MS |
测试点2 | 答案正确 | 652KB | 7MS |
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",<,&rt);
memset(dp,-1,sizeof(dp));
ret=((solve(rt)-solve(lt-1))%mod+mod)%mod;//避免出现负数的常见手法
printf("%lld\n",ret);
}
return 0;
}