数位DP 做题小结

HDU 4507

题意:不满足条件的数字的平方和

思路:DP[pos][num][val] 表示做到第pos位,各个数字和%7=num,整个数字%7=val的合法数字平方和是多少。

#include
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define dep(i,a,b) for(int i=b;i>=a;i--)
using namespace std;
#define ll long long
const int N=3e5+5;
const int mod = 1e9+7;
struct node{
	ll cnt,sum,sqr;
	node(ll cnt=-1,ll sum=0,ll sqr=0):cnt(cnt),sum(sum),sqr(sqr){}
}dp[30][7][7];
ll fac[30];
int a[30];
int T;
ll l,r;
ll rd()
{
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int sum(int a, int b) {
    int s = (a + b);
    if (s >= mod) s -= mod;
    return s;
}
int sub(int a, int b) {
    int s = a - b;
    if (s < 0) s += mod;
    return s;
}
int mult(int a, int b) {
    return (1LL * a * b) % mod;
}
void doit()
{
	fac[0]=1;
	rep(i,1,20) fac[i]=mult(fac[i-1],10);
	rep(i,0,20)
		rep(j,0,6)
			rep(k,0,6)
			{
				dp[i][j][k].cnt=-1;
				dp[i][j][k].sum=dp[i][j][k].sqr=0;
			}
}
ll dfs(int pos,int num,int val,ll &cnt,ll &summ,bool limit)
{
	if(pos==-1)
	{
		if(num==0 || val==0)	return 0;
		cnt=1;
		return 0;
	}
	if(!limit && dp[pos][num][val].cnt!=-1)
	{
		cnt=dp[pos][num][val].cnt;
		summ=dp[pos][num][val].sum;
		return dp[pos][num][val].sqr;
	}
	int up=limit?a[pos]:9;
	ll sq=0;
	rep(i,0,up)
	if(i!=7) 
	{
		ll cn=0,su=0;
		ll x=dfs(pos-1,(num+i)%7,(val*10+i)%7,cn,su,limit &&i==a[pos]);
		int mi=mult(i,fac[pos]),xy=mult(2,mult( fac[pos],mult( i,su )));
		//printf("%d %d %d %d %d\n",mi,xy,pos,i,fac[1]);
		sq=sum( sq,sum( x,sum( mult( cn,mult(mi,mi) ),xy ) ) );
		cnt=sum(cnt,cn);
		summ=sum(summ,sum( su,mult(mi,cn) ) );
		//printf("sq=%lld cnt=%lld summ=%lld\n",sq,cnt,summ);
	}
	if(!limit) dp[pos][num][val]={cnt,summ,sq};
	return sq;
}
int solve(ll x)
{
	int pos=0;
	while(x)
	{
		a[pos++]=x%10;
		x/=10; 
	}
	//printf("   :%d\n",pos);
	ll t1=0,t2=0;
	return dfs(pos-1,0,0,t1,t2,true)%mod;
}
int main()
{
	doit();
	T=rd();
	while(T--)
	{
		l=rd();r=rd();
		printf("%lld\n",sub(solve(r),solve(l-1)));
	}
}

 

HDU 6659

部分题意:f(d,k)表示1-k之间的所有数字数位上出现过d的个数是多少

思路:直接数位DP


#include
#include
#include
#include
using namespace std;
typedef long long ll;
struct node{
	long long a;long long b;
};
int x=6;
ll a[20];
ll dp[20][2];
ll num[20][2];
node dfs(int pos,int pre,bool limit)
{
	//printf("%d %d pre=%d\n",pos,limit,pre);
    if(pos==-1) return {0,1};
    if(!limit && num[pos][pre]!=-1) 
	{
		return {dp[pos][pre],num[pos][pre]};
	}
    int up=limit ? a[pos] : 9;
    ll tmp=0,tmp1=0;
    for(int i=0;i<=up;i++)
    {
    	node ans=dfs(pos-1,i==x,limit && i==a[pos]);
        tmp+=ans.a;
        if(i==x) tmp+=ans.b;
        tmp1+=ans.b;
    }
    if(!limit) 
	{
		dp[pos][pre]=tmp;
		num[pos][pre]=tmp1;
	}
    return {tmp,tmp1};
}
ll solve(ll x)
{
    int pos=0;
    //printf("%lld\n",x);
    while(x)
    {
        a[pos++]=x%10;
        x/=10;
    }
    return dfs(pos-1,0,true).a;
}
int main()
{
    ll le,ri;
    while(~scanf("%lld",&ri))
    {
        memset(num,-1,sizeof(num));
        printf("%lld\n",solve(ri));
    }
    return 0;
}
/*
1000000000000
*/

 

 

你可能感兴趣的:(DP,数位DP)