传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=3131
思路:人生第一道数位dp,,,解锁了人生新成就,,,
数位dp的一般思路,分为两步:1.dp预处理 2. 统计答案
然而第二步往往非常恶心,,,
一般来讲,第二步是根据位数从前向后统计第一个当前位数小于给定n的位置,,,这一道题的idea在于一个数的各个位数都是1 - 9
如果按质因数来看只有2 3 5 7,实际网上大部分都是爆搜所有可能性再dp(貌似总状态非常少啊,,),然而我直接数位dp保存了,因为状态的可行性挂了好久
感觉恶心,,,
代码:
#include
#include
#include
#include
#include
#define N 15
#define max2 41
#define max3 27
#define max5 19
#define max7 16
#define maxn 2320500
#include
using namespace std;
typedef long long LL;
struct node { LL loc1,loc2,value;};
node heap[maxn + 5];
double limit;
LL n,k,P,f[N + 5][max2 + 5][max3 + 5][max5 + 5][max7 + 5],ans[max2 + 5][max3 + 5][max5 + 5][max7 + 5],
p[10][4],g[4],len,w[N + 5],cnt,num,q[maxn + 5],real_ans,maxa,maxb,maxc,maxd,r[4][max2 + 5];
bool can[max2 + 5][max3 + 5][max5 + 5][max7 + 5];
inline bool cmp(LL a,LL b){ return (a > b); }
void init(){
memset(can,0,sizeof(can));
scanf("%lld%lld",&n,&k); P = 1000000007LL; limit = log(n);
maxa = max2 ; maxb = max3;
maxc = max5; maxd = max7;
memset(f,0,sizeof(f));
f[1][0][0][0][0] = 1; f[1][1][0][0][0] = 1; f[1][0][1][0][0] = 1;
f[1][2][0][0][0] = 1; f[1][0][0][1][0] = 1; f[1][1][1][0][0] = 1;
f[1][0][0][0][1] = 1; f[1][3][0][0][0] = 1; f[1][0][2][0][0] = 1;
p[2][0] = 1; p[3][1] = 1; p[4][0] = 2;
p[5][2] = 1; p[6][0] = 1; p[6][1] = 1;
p[7][3] = 1; p[8][0] = 3; p[9][1] = 2;
r[0][0] = r[1][0] = r[2][0] = r[3][0] = 1;
for (int i = 1;i <= maxa; ++i) r[0][i] = r[0][i-1] * 2;
for (int i = 1;i <= maxb; ++i) r[1][i] = r[1][i-1] * 3;
for (int i = 1;i <= maxc; ++i) r[2][i] = r[2][i-1] * 5;
for (int i = 1;i <= maxd; ++i) r[3][i] = r[3][i - 1] * 7;
}
inline bool check(LL a,LL b,LL c,LL d){
LL sum = 1; LL x[4] = {a,b,c,d};
for (int j = 0;j < 4; ++j){
sum = sum * r[j][x[j]];
if (sum > n) return 0;
}
return 1;
}
void dp(){
len = 0;
LL m = n;
while (m) {w[++len] = m % 10;m = m / 10; }
for (int i = 1;i < len; ++i){
for (int a = 0; a <= maxa; ++a)
for (int b = 0; b <= maxb; ++b)
for (int c = 0;c <= maxc; ++c)
for (int d = 0; d <= maxd; ++d)
if (!check(a,b,c,d)) break;
else
{
for (int k = 1;k <= 9; ++k)
f[i + 1][a + p[k][0]][b + p[k][1]][c + p[k][2]][d + p[k][3]] += f[i][a][b][c][d];}
}
for (int i = 1;i < len; ++i)
for (int a = 0; a <= maxa; ++a)
for (int b = 0; b <= maxb; ++b)
for (int c = 0;c <= maxc; ++c)
for (int d = 0; d <= maxd; ++d)
if (!check(a,b,c,d)) break;
else ans[a][b][c][d] += f[i][a][b][c][d];
for (int i = len;i > 1; --i){
LL t = w[i];
if (!t) break;
for (int j = 1;j < t; ++j)
for (int a = 0; a <= maxa; ++a)
for (int b = 0; b <= maxb; ++b)
for (int c = 0;c <= maxc; ++c)
for (int d = 0; d <= maxd; ++d)
if (!check(a,b,c,d)) break;
else
{
LL t1 = f[i - 1][a][b][c][d];
if (check(a + g[0] + p[j][0],b + g[1] + p[j][1],c + g[2] + p[j][2],d + g[3] + p[j][3]))
ans[a + g[0] + p[j][0]][b + g[1] + p[j][1]][c + g[2] + p[j][2]][d + g[3] + p[j][3]] += t1;}
for (int j = 0;j < 4; ++j) g[j] += p[w[i]][j];
}
bool flag = 1;
for (int i = 1;i <= len; ++i) if (!w[i]) flag = 0;
if (flag) for (int j = 1;j <= w[1]; ++j)
ans[g[0] + p[j][0]][g[1] + p[j][1]][g[2] + p[j][2]][g[3] + p[j][3]] ++;
}
inline void push(LL x,LL y){
heap[++cnt] = (node){x,y,q[x] * q[y]};
LL j = cnt;
while (1){
if (j == 1||heap[j].value < heap[j>>1].value) break;
swap(heap[j],heap[j>>1]);
j >>= 1;
}
}
inline node pop(){
node val = heap[1];
heap[1] = heap[cnt--];
LL j = 1;
if (cnt){
while (1){
LL maxist = j;
if ((j << 1)<= cnt&& heap[j<<1].value > heap[maxist].value) maxist = j<<1;
if ((j<<1|1)<=cnt&&heap[j<<1|1].value > heap[maxist].value) maxist = j<<1|1;
if (maxist == j) break;
swap(heap[j],heap[maxist]);
j = maxist;
}
}
return val;
}
void get_ans(){
LL maxi = 0;
num = 0; cnt = 0; real_ans = 0;
for (int a = 0; a <= maxa; ++a)
for (int b = 0; b <= maxb; ++b)
for (int c = 0;c <= maxc; ++c)
for (int d = 0; d <= maxd; ++d)
if (!check(a,b,c,d)) break;
else if (ans[a][b][c][d]) {
q[++num] = ans[a][b][c][d];}
sort(q + 1,q + num + 1,cmp);
for (int i = 1;i <= num; ++i) push(i,1);
for (int i = 1;i <= k; ++i)
if (cnt > 0)
{
node t = pop();
real_ans = (real_ans + t.value % P) % P;
if (t.loc2 < num) push(t.loc1,t.loc2 + 1);
}
}
void DO_IT(){
dp();
get_ans();
}
int main(){
init();
DO_IT();
cout<return 0;
}
总结:1.注意读入和输出用I64D还是lld
2.注意审题,看清条件 3.保持思维严谨,不能混乱