topcoder: TCHS10 Championship Round DIV 1 (lucky number third level)

Problem Statement

 

The digits 4 and 7 are lucky digits, and all other digits are unlucky. A third level lucky number is a positive integer that is divisible by at least one lucky digit. John and Brus have written down all the third level lucky numbers between a and b, inclusive, and now they would like to count the number of occurrences of each digit between 0 and 9, inclusive, in the numbers they have written.

Return a vector<long long> containing exactly 10 elements, where the i-th element (0-based) is the total number of occurrences of digit i.

Definition

 
Class:TheLuckyNumbersLevelThree
Method:find
Parameters:long long, long long
Returns:vector<long long>
Method signature:vector<long long> find(long long a, long long b)
(be sure your method is public)

Limits

 
Time limit (s):2.000
Memory limit (MB):64

Constraints

-a will be between 1 and 10^16, inclusive.
-b will be between a and 10^16, inclusive.

数位dp的题居然也能压轴~~服了。

题意:列出区间[a,b]所有能被4或者7整除的数,统计这些数含有多少个digit s,s:0,1,...,9

example:

a = 1, b = 10

Returns: {0, 0, 0, 0, 1, 0, 0, 1, 1, 0 }
There are three third level lucky numbers in this range - 4, 7, 8.

tricky: 传统的数位dp一般数统计满足条件的数的个数,但是这个题统计的是满足条件的数中digit 0-9的个数。

一开始,如果不仔细分析到这里就很容易跳进死胡同,其实此题可以很简单的转换到常规数位dp

换一种问法:区间[a,b]中所有满足条件的数x:

x能被4或者7整除的

x含有k个1

这里就可以枚举 k 和digit 0-9。

然后,设计数位dp的递归函数就比较方便了。


#include<cmath>
#include<cstdlib>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<vector>
using namespace std;

#define FOR(i,a,b) for(int i=a;i<b;i++)
#define FORE(i,a,b) for(int i=a;i<=b;i++)
#define MST(a,b) memset(a,b,sizeof(a))

class TheLuckyNumbersLevelThree{
	public:
	int cur,n;
	int d[17];
	int tmpsum;
	long long dp[17][17][7][4];
	int mod4[17],mod7[17];
	TheLuckyNumbersLevelThree(){
		mod4[0] = mod7[0] = 1;
		FOR(i,1,17) mod4[i] = mod4[i-1]*10%4;
		FOR(i,1,17) mod7[i] = mod7[i-1]*10%7;
		tmpsum = 0;
	}

	long long dfs(int i,int c,int u,int v,bool flag) {
	if(i==-1){
		if(!u || !v)	return c;
		else return 0;
	}
	if(!flag && tmpsum && dp[i][c][u][v]!=-1 )
		return  dp[i][c][u][v];
	long long ans = 0;
	int lim = (flag?d[i]:9);
	FORE(s,0,lim){
		tmpsum += s;
		ans += dfs(i-1,c+((s==cur) && tmpsum),(u+s*mod7[i])%7,(v+s*mod4[i])%4,flag && (s==lim));
		tmpsum -= s;
	}
	if(!flag && tmpsum) dp[i][c][u][v] = ans;
	return ans;
	}

	vector<long long> find(long long a, long long b){
		long long sa,sb,x;
		vector<long long>  ans;
		FOR(i,0,10){
			cur = i;
			for(x = a-1,n = 0;x;x/=10)
				d[n++]=x%10;
			MST(dp,-1);
			sa = dfs(n-1,0,0,0,1);

			for(x = b,n = 0;x;x/=10)
				d[n++]=x%10;
			MST(dp,-1);
			sb = dfs(n-1,0,0,0,1);
			ans.push_back(sb-sa);
		}
		return ans;
	}
};






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值