【HDU4507】恨7不成妻(数位DP)

点此看题面

大致题意: 让你求出一段区间内与 7 7 7无关的数的平方和。与 7 7 7无关的数指整数中任意一位不为 7 7 7整数的每一位加起来的和不是 7 7 7的整数倍这个整数不是 7 7 7的倍数


数位 D P DP DP

这题应该比较显然是一道 数位 D P DP DP 题。

L i n k Link Link

数位 D P DP DP 详见博客 动态规划专题(三)——数位DP


如何记录状态

这道题关键就在于如何记录状态,其余的就和普通的数位 D P DP DP差不多了。

我们可以用 f x , s 1 , s 2 f_{x,s1,s2} fx,s1,s2来表示还剩 x x x位,这个数除末 x x x位以外模 7 7 7 s 1 s1 s1,这个数每一位之和除末 x x x位以外模 7 7 7 s 2 s2 s2时所有与 7 7 7无关的数的 x x x的平方和。

但是,如果光光记录平方和,转移就有点困难了。

所以,我们先要来一点恶心的数学转化。


数学转化

让我们来研究一下 ( x 1 + t ∗ 1 0 y ) 2 + ( x 2 + t ∗ 1 0 y ) 2 + . . . + ( x n + t ∗ 1 0 y ) 2 (x_1+t*10^y)^2+(x_2+t*10^y)^2+...+(x_n+t*10^y)^2 (x1+t10y)2+(x2+t10y)2+...+(xn+t10y)2这个式子。

先由完全平方公式可得:

原 式 = ( x 1 2 + 2 ∗ x 1 ∗ t ∗ 1 0 y + 1 0 2 y ) + ( x 2 2 + 2 ∗ x 2 ∗ t ∗ 1 0 y + 1 0 2 y ) + . . . + ( x n 2 + 2 ∗ x n ∗ t ∗ 1 0 y + 1 0 2 y ) 原式=(x_1^2+2*x_1*t*10^y+10^{2y})+(x_2^2+2*x_2*t*10^y+10^{2y})+...+(x_n^2+2*x_n*t*10^y+10^{2y}) =(x12+2x1t10y+102y)+(x22+2x2t10y+102y)+...+(xn2+2xnt10y+102y)

然后,我们将其去括号并重新组合,可得:

原 式 = ( x 1 2 + x 2 2 + . . . + x n 2 ) + 2 ∗ t ∗ 1 0 y ∗ ( x 1 + x 2 + . . . + x n ) + ( t ∗ 1 0 y ) 2 ∗ n 原式=(x_1^2+x_2^2+...+x_n^2)+2*t*10^y*(x_1+x_2+...+x_n)+(t*10^y)^2*n =(x12+x22+...+xn2)+2t10y(x1+x2+...+xn)+(t10y)2n

如果用 f ( n ) f(n) f(n)来表示 x 1 + x 2 + . . . + x n x_1+x_2+...+x_n x1+x2+...+xn f 2 ( n ) f^2(n) f2(n)来表示 x 1 2 + x 2 2 + . . . + x n 2 x_1^2+x_2^2+...+x_n^2 x12+x22+...+xn2,则:

原 式 = f 2 ( n ) + 2 ∗ t ∗ 1 0 y ∗ f ( n ) + ( t ∗ 1 0 y ) 2 ∗ n 原式=f^2(n)+2*t*10^y*f(n)+(t*10^y)^2*n =f2(n)+2t10yf(n)+(t10y)2n

我们可以预处理出 1 0 y 10^y 10y,并对于每个状态记录下 n , f ( n ) n,f(n) n,f(n) f 2 ( n ) f^2(n) f2(n),这样就可以实现 O ( 1 ) O(1) O(1)转移了。


状态转移方程

n s 1 ns1 ns1来表示 ( s 1 ∗ 10 + i ) (s1*10+i) (s110+i)% y y y n s 2 ns2 ns2来表示 ( s 2 + i ) (s2+i) (s2+i)% y y y

n x , s 1 , s 2 = ∑ i = 0 l i m n x − 1 , n s 1 , n s 2 n_{x,s1,s2}=\sum_{i=0}^{lim}n_{x-1,ns1,ns2} nx,s1,s2=i=0limnx1,ns1,ns2

f x , s 1 , s 2 = ∑ i = 0 l i m f x − 1 , n s 1 , n s 2 + n x − 1 , n s 1 , n s 2 ∗ i ∗ 1 0 x − 1 f_{x,s1,s2}=\sum_{i=0}^{lim}f_{x-1,ns1,ns2}+n_{x-1,ns1,ns2}*i*10^{x-1} fx,s1,s2=i=0limfx1,ns1,ns2+nx1,ns1,ns2i10x1

f x , s 1 , s 2 2 = ∑ i = 0 l i m f x − 1 , n s 1 , n s 2 2 + 2 ∗ i ∗ 1 0 x ∗ f x − 1 , n s 1 , n s 2 + ( i ∗ 1 0 x − 1 ) 2 ∗ n x − 1 , n s 1 , n s 2 f^2_{x,s1,s2}=\sum_{i=0}^{lim}f^2_{x-1,ns1,ns2}+2*i*10^x*f_{x-1,ns1,ns2}+(i*10^{x-1})^2*n_{x-1,ns1,ns2} fx,s1,s22=i=0limfx1,ns1,ns22+2i10xfx1,ns1,ns2+(i10x1)2nx1,ns1,ns2


代码
#include<bits/stdc++.h>
#define max(x,y) ((x)>(y)?(x):(y))
#define min(x,y) ((x)<(y)?(x):(y))
#define uint unsigned int
#define LL long long
#define ull unsigned long long
#define swap(x,y) (x^=y,y^=x,x^=y)
#define abs(x) ((x)<0?-(x):(x))
#define INF 1e9
#define Inc(x,y) ((x+=y)>=MOD&&(x-=MOD))
#define MOD 1000000007
using namespace std;
LL n,m;
class FIO
{
	private:
		#define Fsize 100000
		#define tc() (FinNow==FinEnd&&(FinEnd=(FinNow=Fin)+fread(Fin,1,Fsize,stdin),FinNow==FinEnd)?EOF:*FinNow++)
		#define pc(ch) (FoutSize<Fsize?Fout[FoutSize++]=ch:(fwrite(Fout,1,FoutSize,stdout),Fout[(FoutSize=0)++]=ch))
		LL f,FoutSize,OutputTop;char ch,Fin[Fsize],*FinNow,*FinEnd,Fout[Fsize],OutputStack[Fsize];
	public:
		FIO() {FinNow=FinEnd=Fin;}
		inline void read(LL &x) {x=0,f=1;while(!isdigit(ch=tc())) f=ch^'-'?1:-1;while(x=(x<<3)+(x<<1)+(ch&15),isdigit(ch=tc()));x*=f;}
		inline void read_char(char &x) {while(isspace(x=tc()));}
		inline void read_string(string &x) {x="";while(isspace(ch=tc()));while(x+=ch,!isspace(ch=tc())) if(!~ch) return;}
		inline void write(LL x) {if(!x) return (void)pc('0');if(x<0) pc('-'),x=-x;while(x) OutputStack[++OutputTop]=x%10+48,x/=10;while(OutputTop) pc(OutputStack[OutputTop]),--OutputTop;}
		inline void write_char(char x) {pc(x);}
		inline void write_string(string x) {register LL i,len=x.length();for(i=0;i<len;++i) pc(x[i]);}
		inline void end() {fwrite(Fout,1,FoutSize,stdout);}
}F;
class Class_DigitalDP
{
	private:
		#define ten(x) ((x<<3)+(x<<1))
		LL ans,len,num[20],tn[20];
  		struct key//记录一个状态
		{
			LL res,res_,tot;//res记录平方和,res_记录和,tot记录个数
			key(LL x=0,LL y=0,LL z=-1):res(x%MOD),res_(y%MOD),tot(z%MOD){}
		}f[20][7][7];
		inline void Init(LL x) {len=0;while(x) num[++len]=x%10,x/=10;num[len+1]=0;}
		inline key dfs(LL x,LL s1,LL s2,LL flag)
		{
			register LL i,lim=9,k;register key w=key(0,0,0),t;//w记录结果
			if(!x) return key(0,0,s1&&s2);//如果x为0,返回结果
			if(flag&&~f[x][s1][s2].tot) return f[x][s1][s2];//如果当前状态肯定在求解范围内,且已经访问并求解过当前状态,返回上次求解得到的答案
			if(!flag) (num[x]^7&&(t=dfs(x-1,(ten(s1)+num[x])%7,(s2+num[x])%7,0),k=num[x]*tn[x-1]%MOD,w=key(t.res+(t.res_<<1)*k%MOD+t.tot*k%MOD*k%MOD,t.res_+t.tot*k%MOD,t.tot),true)),lim=num[x]-1;//对于不一定在求解范围内的值特殊处理
			for(i=0;i<=lim;++i) if(i^7) t=dfs(x-1,(ten(s1)+i)%7,(s2+i)%7,1),k=i*tn[x-1]%MOD,w=key(w.res+t.res+(t.res_<<1)*k%MOD+t.tot*k%MOD*k%MOD,w.res_+t.res_+t.tot*k%MOD,w.tot+t.tot);//对每一个不为7的数字进行状态转移
			if(flag) f[x][s1][s2]=w;//如果当前状态肯定在求解的范围内,就将求解出的答案记录下来,实现记忆化
			return w;//返回求解出的答案
		}
	public:
		Class_DigitalDP() {tn[0]=1;for(register LL i=1;i<20;++i) tn[i]=ten(tn[i-1])%MOD;}//预处理出10的幂
		inline LL GetAns(LL x) {return !x?0:((void)(Init(x)),dfs(len,0,0,0).res);}//求解答案
}DigitalDP;
int main()
{
    register LL i;register LL T;F.read(T);
    while(T--) F.read(n),F.read(m),F.write(((DigitalDP.GetAns(m)-DigitalDP.GetAns(n-1))%MOD+MOD)%MOD),F.write_char('\n');
    return F.end(),0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值