[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
",((dp(m)-dp(n-1))%mod+mod)%mod);	
	}
	return 0;
}
原文地址:https://www.cnblogs.com/Chtholly/p/11203662.html