[HDU4507]恨7不成妻(数位dp)
题意
给定区间[ l , r ](\(1 \leq l < r \leq 1e18\)),求在区间中满足下列条件的所有数x的平方和:
不存在数字7;不是7的倍数;每一位的数字的和也不是7的倍数
思路
数位dp
状态:位置i,数对7取模j,各个位的和对7取模(,是否顶上界)
下面的代码用记忆化搜索方式实现,从低位向高位递进。
已知后面i位数满足的平方和,再在前面加入一个数怎么办
维护后面i位的平方和pow,由\((a+i)^2\)=\(a^2+2ai+i^2\),已知\(i^2\),那么再加上\(a^2+2ai\)即可,由于要用到'i',所以还要维护后面i位的和sum。另外,由于后i位可能有很多满足条件的数,所以还要维护个数cnt
转移有很多细节,比之前写的睿(ruo)智数位dp确实难了一些
Code:
#include<bits/stdc++.h>
#define N 25
using namespace std;
typedef long long ll;
const ll mod = 1000000007;
int T,a[N],sum;
ll n,m,x,temp[N];
struct Node
{
ll cnt,sum,po;
Node() {cnt=-1;sum=po=0;}
Node(ll c,ll s,ll p) : cnt(c),sum(s),po(p) {}
}f[N][8][8];//无限制lim才能用的记忆化
template <class T>
void read(T &x)
{
char c;int sign=1;
while((c=getchar())>'9'||c<'0') if(c=='-') sign=-1; x=c-48;
while((c=getchar())>='0'&&c<='9') x=(x<<1)+(x<<3)+c-48; x*=sign;
}
Node dfs(int pos,int wei,int sum,int lim)//在pos填数,位数和,数,顶上界
{
if(!pos) return wei==0||sum==0 ? Node(0,0,0):Node(1,0,0);
if(f[pos][wei][sum].cnt!=-1&&!lim) return f[pos][wei][sum];
int mx=lim ? a[pos] : 9;
Node now=Node(0,0,0);
for(int i=0;i<=mx;++i)//注意细节
{
if(i==7) continue;
Node nxt=dfs(pos-1,(wei+i)%7,(sum*10+i)%7,lim&&(i==mx));
now.cnt=(now.cnt+nxt.cnt)%mod;
now.sum=(now.sum+(i*temp[pos]%mod*nxt.cnt%mod+nxt.sum)%mod)%mod;
//原先的sum+单选i的贡献*后面的cnt(即i的重复次数)+后面的sum
now.po=(now.po + nxt.po + i*temp[pos]%mod*i%mod*temp[pos]%mod*nxt.cnt%mod)%mod;
now.po=(now.po+2*i*temp[pos]%mod*nxt.sum%mod)%mod;
//计算nxt.sum时已经重复过nxt.cnt次所以这一步不用乘nxt.cnt
}
if(!lim) f[pos][wei][sum]=now;
return now;
}
ll dp(ll maxx)
{
sum=0; x=maxx;
while(x) {a[++sum]=x%10;x/=10;}
return dfs(sum,0,0,1).po;
}
int main()
{
// freopen("seven.in","r",stdin);
// freopen("seven.out","w",stdout);
read(T);
temp[1]=1LL;
for(int i=2;i<=20;++i) temp[i]=temp[i-1]*10%mod;
while(T--)
{
read(n);read(m);
printf("%lld\n",((dp(m)-dp(n-1))%mod+mod)%mod);
}
return 0;
}