3131: [Sdoi2013]淘金

传送门: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.保持思维严谨,不能混乱

你可能感兴趣的:(3131: [Sdoi2013]淘金)