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<iostream>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#define N 15
#define max2 41
#define max3 27
#define max5 19
#define max7 16
#define maxn 2320500
#include<algorithm>
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<<real_ans;
    return 0;
}

总结:1.注意读入和输出用I64D还是lld
2.注意审题,看清条件 3.保持思维严谨,不能混乱

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要实现这个功能,你可以使用淘金池串口屏的按钮事件和图片切换功能。具体步骤如下: 1. 首先,你需要在淘金池串口屏上设置一个按钮事件,当按钮被按下时触发该事件。 2. 然后,你需要在代码中编写相应的处理函数,处理按钮事件。在处理函数中,你需要判断当前显示的图片,并根据需要切换到另一个图片。 3. 切换图片的具体操作,可以使用淘金池串口屏提供的图片切换功能。你可以使用 `pic()` 函数来切换图片,该函数接受两个参数,第一个参数是图片的编号,第二个参数是图片显示的时间(以毫秒为单位)。 4. 最后,为了保持另一个图片不动,你需要在切换到另一个图片时,将该图片的显示时间设置为一个非常大的值,比如 9999999。这样就可以让该图片一直保持不动了。 示例代码如下: ``` #include <TJC.h> void setup() { Serial.begin(9600); TJC.begin(); // 设置按钮事件 TJC.setButtonHandler(1, buttonHandler); // 显示第一张图片 TJC.pic(1, 5000); } void loop() { // 主循环 } void buttonHandler(int button) { // 处理按钮事件 int currentPic = TJC.currentPic(); // 获取当前显示的图片编号 if (currentPic == 1) { // 切换到第二张图片,并设置显示时间为 9999999 毫秒 TJC.pic(2, 9999999); } else { // 切换到第一张图片,并设置显示时间为 5000 毫秒 TJC.pic(1, 5000); } } ``` 这段代码会在淘金池串口屏上显示两张图片,当按钮被按下时,会切换到另一个图片并保持不动。注意,这只是一个示例代码,具体实现方式可能会因为硬件和软件环境的不同而有所不同。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值