数位dp:bzoj1799

一开始没有想到枚举所有模数求解,卡了很久。

写法见代码注释。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<stack>
#include<map>
#include<vector>
#include<queue>
#include<set>
#include<iomanip>
#include<cctype> 
#include<ctime>
using namespace std;
#define ll long long
#define edl putchar('\n')
#define sscc ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
#define FOR(i,a,b) for(int i=a;i<=b;i++)
#define ROF(i,a,b) for(int i=a;i>=b;i--)
#define FORLL(i,a,b) for(ll i=a;i<=b;i++)
#define ROFLL(i,a,b) for(ll i=a;i>=b;i--)
#define mst(a) memset(a,0,sizeof(a))
#define mstn(a,n) memset(a,n,sizeof(a))
#define zero(x)(((x)>0?(x):-(x))<eps)
inline ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
inline ll lcm(ll a,ll b){return a/gcd(a,b)*b;}
int month[15]={0,31,28,31,30,31,30,31,31,30,31,30,31};
const int MAXN=1e5+5;
const int INF=1<<30;
const long long mod=1e9+7;
const double eps=1e-8;
ll dp[25][170][170];
/*dp数组记录答案,第一维为第几位,第二维为当前数字模p的结果,第三维为数位和*/ 
int a[25],p,vis[25][170][170];
/*a数组记录每一位的值,p为模数,vis数组对应dp,代表在模p时是否访问过*/ 
ll dfs(int pos,int m,int sta,int limit)/*第几位 模后的数 数位和 上限*/ 
{
	if(pos==0)
		return sta==0&&m%p==0;
	if(!limit&&vis[pos][m][sta]==p)
	{
		return dp[pos][m][sta];
	}
	vis[pos][m][sta]=p;/*更新vis访问标记*/ 
	int up=limit?a[pos]:9;
	ll ans=0;
	FOR(i,0,up)
	{
		if(sta>=i)
		ans+=dfs(pos-1,(m*10+i)%p,sta-i,limit&&i==up);
		/*由于枚举p时数位和是固定的,所以直接用p减去数位和*/ 
	}
	if(!limit)
		dp[pos][m][sta]=ans;
	return ans;
}
ll solve(ll x)
{
    int pos=0,t;
    while(x)
    {
        a[++pos]=x%10;
        x/=10;
    }
    a[pos+1]=0;
    t=pos*9;
    ll ans=0;
    FOR(i,1,t)
    p=i,ans+=dfs(pos,0,i,1);
    return ans;
}
int main()
{
    ll n,m;
    {
    	scanf("%lld%lld",&n,&m);
    	mst(vis); 
        n=solve(n-1);
        mst(vis);/*每次处理都要对vis初始化*/ 
        m=solve(m);
		printf("%lld\n",m-n);
    }
    return 0;
}

  

posted @ 2018-09-21 22:00  诚信肥宅  阅读(125)  评论(0编辑  收藏  举报