bzoj千题计划268:bzoj3131: [Sdoi2013]淘金

http://www.lydsy.com/JudgeOnline/problem.php?id=3131

 

如果已知 s[i]=j 表示有j个<=n数的数码乘积=i

那么就会有 s[a1]*s[a2] 个数 在一阵风之后到(a1,a2)位置

把所有的j用一个数组b存起来,从大到小排序
开始把(1,1)存入堆,表示当前最多的是b[1]*b[1]
每次取出堆顶(i,j),累加 b[i]*b[j],存入(i+1,j),(i,j+1),map 判重

就可以解决前k大之和的问题

i的上限是n,存不下,怎么办

我们发现 n以内 有大量的i 是无用的

只有质因数分解之后 质因子为2、3、5、7 的i 才是合法状态

所以 dp[len][0/1][c2][c3][c5][c7] 表示前len位,是否卡上界,当前i=2^c2 * 3^c3 * 5^c5 * 7^c7 的 j是多少

数位dp

注意不能放0,但是可以有前导0,所以除了最高位,都要加上前导0的贡献,即dp[][0][0][0][0][0]++ (前导0不卡上界)

 

#include<map>
#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>

using namespace std;

typedef long long LL;

const int mod=1e9+7;

LL dp[2][2][41][27][19][16];
int f[10][4];

int a[14];

int cnt;
LL b[10001];

priority_queue< pair<LL,pair<int,int> > >q;
map<pair<int,int>,bool>vis;

void pre()
{
    f[2][0]=1;
    f[3][1]=1;
    f[4][0]=2;
    f[5][2]=1;
    f[6][0]=1;
    f[6][1]=1;
    f[7][3]=1;
    f[8][0]=3;
    f[9][1]=2;
}

void reverse(int len)
{
    int k=len/2;
    for(int i=1;i<=k;++i) swap(a[i],a[len-i+1]);
}

void ADD(int &x,int y)
{
    x+=y;
    x-=x>=mod ? mod : 0;
}

LL Pow(LL a,int b)
{
    LL res=1;
    for(;b;a*=a,b>>=1)
        if(b&1) res*=a;
    return res;
}

void numberDP(LL n)
{
    int len=0;
    LL m=n;
    while(m) a[++len]=m%10,m/=10;
    reverse(len);
    int up,J;
    int now=0,nxt=1;
    for(int i=0;i<len;++i,swap(now,nxt))
    {
        if(!i) dp[now][1][0][0][0][0]++;
        else dp[now][0][0][0][0][0]++;
        memset(dp[nxt],0,sizeof(dp[nxt]));
        for(int j=0;j<=1;++j)
            for(int c2=0;c2<=40;++c2)
                for(int c3=0;c3<=26;++c3)
                    for(int c5=0;c5<=18;++c5)
                        for(int c7=0;c7<=15;++c7)
                            if(dp[now][j][c2][c3][c5][c7])
                            {
                                if(j) up=a[i+1];
                                else up=9;
                                for(int k=1;k<=up;++k)
                                {
                                    J=(j && k==up);
                                    dp[nxt][J][c2+f[k][0]][c3+f[k][1]][c5+f[k][2]][c7+f[k][3]]+=dp[now][j][c2][c3][c5][c7];
                                }
                            }
    }
    LL sum,tmp;
    for(int c2=0;c2<=40;++c2)
    {
        tmp=Pow(2,c2);
        if(tmp>n) break;
        for(int c3=0;c3<=26;++c3)
        {
            tmp=Pow(2,c2)*Pow(3,c3);
            if(tmp>n) break;
            for(int c5=0;c5<=18;++c5)
            {
                tmp=Pow(2,c2)*Pow(3,c3)*Pow(5,c5);
                if(tmp>n) break;
                for(int c7=0;c7<=15;++c7)
                {
                    tmp=Pow(2,c2)*Pow(3,c3)*Pow(5,c5)*Pow(7,c7);
                    if(tmp>n) break;
                    sum=dp[now][0][c2][c3][c5][c7]+dp[now][1][c2][c3][c5][c7];
                    if(sum) b[++cnt]=sum;/*,printf("%d %d %d %d %I64d\n",c2,c3,c5,c7,sum)*/;
                }
            }
        }
    }
}

void get_kth(int k)
{
    sort(b+1,b+cnt+1,greater<int>());
    int i=1,j=1;
    int ans=0;
    pair<LL,pair<int,int> >pr;
    int x,y;
    q.push(make_pair(b[1]*b[1],make_pair(1,1)));
    while(k-- && !q.empty())
    {
        pr=q.top();
        q.pop();
        if(!pr.first) break;
        ADD(ans,pr.first%mod);
        x=pr.second.first;
        y=pr.second.second;
        if(!vis[make_pair(x+1,y)]) 
        {
            q.push(make_pair(b[x+1]*b[y],make_pair(x+1,y)));
            vis[make_pair(x+1,y)]=true;
        }
        if(!vis[make_pair(x,y+1)]) 
        {
            q.push(make_pair(b[x]*b[y+1],make_pair(x,y+1)));
            vis[make_pair(x,y+1)]=true;
        }
    }
    printf("%d",ans);
}

int main()
{
    //freopen("gold.in","r",stdin);
    //freopen("gold.out","w",stdout);
    LL n; int k;
    scanf("%lld%d",&n,&k);
    pre();
    numberDP(n);
    get_kth(k);
    return 0;
}

 

转载于:https://www.cnblogs.com/TheRoadToTheGold/p/8536499.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: bzoj作为一个计算机竞赛的在线评测系统,不仅可以提供大量的题目供程序员练习和学习,还可以帮助程序员提升算法和编程能力。为了更好地利用bzoj进行题目的学习和刷题,制定一个bzoj做题计划是非常有必要的。 首先,我们需要合理安排时间,每天留出一定的时间来做bzoj的题目。可以根据自己的时间安排,每天挑选适量的题目进行解答。可以先从难度较低的题目开始,逐渐提高难度,这样既能巩固基础知识,又能挑战自己的思维能力。 其次,要有一个计划和目标。可以规划一个每周或每月的题目数量目标,以及每个阶段要学习和掌握的算法知识点。可以根据bzoj的题目分类,如动态规划、图论、贪心算法等,结合自己的实际情况,有针对性地选择题目进行学习。 此外,要充分利用bzoj提供的资源。bzoj网站上有很多高质量的题解和优秀的解题代码,可以参考和学习。还有相关的讨论区,可以与其他程序员交流和讨论,共同进步。 最后,要坚持并保持思考。做题不是单纯为了刷数量,更重要的是学会思考和总结。遇到难题时,要有耐心,多思考,多尝试不同的解法。即使不能一次性解出来,也要学会思考和分析解题过程,以及可能出现的错误和优化。 总之,bzoj做题计划的关键在于合理安排时间、制定目标、利用资源、坚持思考。通过有计划的刷题,可以提高算法和编程能力,并培养解决问题的思维习惯,在计算机竞赛中取得更好的成绩。 ### 回答2: bzoj做题计划是指在bzoj这个在线测评系统上制定一套学习和刷题的计划,并且将计划记录在excel表格中。该计划主要包括以下几个方面的内容。 首先是学习目标的设定。通过分析自己的水平和知识缺口,可以设定一个合理的目标,比如每天解决一定数量的题目或者提高特定的算法掌握程度。 其次是题目选择的策略。在excel表格中可以记录下自己选择的题目编号、题目类型和难度等信息。可以根据题目的类型和难度来安排每天的刷题计划,确保自己可以逐步提高技巧和解题能力。 然后是学习进度的记录和管理。将每天的完成情况记录在excel表格中,可以清晰地看到自己的学习进度和任务完成情况。可以使用图表等功能来对学习进度进行可视化展示,更好地管理自己的学习计划。 同时,可以在excel表格的备注栏中记录下每道题目的解题思路、关键点和需要复习的知识点等信息。这样可以方便自己回顾和总结,巩固所学的知识。 最后,可以将excel表格与其他相关资料进行整合,比如算法教材、题目解析和学习笔记等。这样可以形成一个完整的学习档案,方便自己进行系统的学习和复习。 总之,bzoj做题计划excel的制定和记录可以帮助我们更加有条理和高效地进行学习和刷题。通过合理安排学习目标和题目选择策略,记录学习进度和思路,并整合其他学习资料,我们可以提高自己的解题能力,并在bzoj上取得更好的成绩。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值