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

时间: 2024-10-31 04:45:11

bzoj千题计划268:bzoj3131: [Sdoi2013]淘金的相关文章

bzoj千题计划185:bzoj1260: [CQOI2007]涂色paint

http://www.lydsy.com/JudgeOnline/problem.php?id=1260 区间DP模型 dp[l][r] 表示涂完区间[l,r]所需的最少次数 从小到大们枚举区间[l,r] 如果col[l]==col[r] dp[l][r]=min(dp[l+1][r],dp[l][r-1],dp[l+1][r-1]+1) 否则 dp[l][r]=min(dp[l][k]+dp[k+1][r]) 我还是辣鸡啊~~~~(>_<)~~~~,这种题都不能秒 #include<c

bzoj千题计划292:bzoj2244: [SDOI2011]拦截导弹

http://www.lydsy.com/JudgeOnline/problem.php?id=2244 每枚导弹成功拦截的概率 = 包含它的最长上升子序列个数/最长上升子序列总个数 pre_len [i] 表示以i结尾的最长不下降子序列的长度 pre_sum[i] 表示对应长度下的方案数 suf_len[i] 表示以i开头的最长不下降子序列长度 suf_sum[i] 表示对应长度下的方案数 若已有了这4个数组 设最长上升子序列长度=mx 那么 如果pre_len[i]+suf_len[i] -

bzoj千题计划304:bzoj3676: [Apio2014]回文串

https://www.lydsy.com/JudgeOnline/problem.php?id=3676 回文自动机模板题 4年前的APIO如今竟沦为模板,,,╮(╯▽╰)╭,唉 #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define N 300001 char ss[N]; int s[N]; int tot=1,last; int fail[N],len

bzoj千题计划106:bzoj1014 [JSOI2008]火星人prefix

http://www.lydsy.com/JudgeOnline/problem.php?id=1014 两个后缀的最长公共前缀:二分+hash 带修改带插入:splay维护 #include<cstdio> #include<cstring> #include<iostream> #define L 100001 typedef unsigned long long ULL; using namespace std; char s[L+4]; int tot,root

bzoj千题计划108:bzoj1018: [SHOI2008]堵塞的交通traffic

http://www.lydsy.com/JudgeOnline/problem.php?id=1018 关键点在于只有两行 所以一个2*m矩形连通情况只有6种 编号即对应代码中的a数组 线段树维护 用b数组表示 节点第0/1行的最右一列是否连接了右边 来 辅助 节点的合并 查询 对两个点位于矩形的位置分4种情况讨论 两点是否联通,要考虑四种情况 (以两个位置是矩形左上角和右上角为例) 1.直接联通,线段树的节点包含了这种情况,直接判断 2. 3. 4. 后三种情况需要再查询[1,l]和[r,n

bzoj千题计划109:bzoj1019: [SHOI2008]汉诺塔

http://www.lydsy.com/JudgeOnline/problem.php?id=1019 题目中问步骤数,没说最少 可以大胆猜测移动方案唯一 (真的是唯一但不会证) 设f[i][j] 表示 从i号柱子 上把j个盘子移到 g[i][j] 柱子上的步数 初始化:f[0][1]=1,g[0][1] 根据优先级决定 设三根柱子分别为0,1,2 对于每一个f[x][i], 把前i-1个移走,把第i个移走,把前i-1个移回 令y=g[x][i-1],则k=0+1+2-x-y 我们希望 把i-

bzoj千题计划111:bzoj1021: [SHOI2008]Debt 循环的债务

http://www.lydsy.com/JudgeOnline/problem.php?id=1021 如果A收到了B的1张10元,那么A绝对不会把这张10元再给C 因为这样不如B直接给C优 由此可以推出 若A欠B20元,B欠C 30元, 那么A还C20元,B还C10元最优 所以一共只有 A->BC   B->AC  C->AB AB->C  BC->A  AC->B 这6种转移情况 根据输入,我们可以知道三人最终手中有多少钱ea.eb.ec,一共有多少钱sum 设f

bzoj千题计划112:bzoj1022: [SHOI2008]小约翰的游戏John

http://www.lydsy.com/JudgeOnline/problem.php?id=1022 http://www.cnblogs.com/TheRoadToTheGold/p/6744825.html #include<cstdio> #include<iostream> using namespace std; void read(int &x) { x=0; char c=getchar(); while(!isdigit(c)) c=getchar();

bzoj千题计划113:bzoj1023: [SHOI2008]cactus仙人掌图

http://www.lydsy.com/JudgeOnline/problem.php?id=1023 dp[x] 表示以x为端点的最长链 子节点与x不在同一个环上,那就是两条最长半链长度 子节点与x在同一个环上,环形DP,单调队列优化 对于每一个环,深度最小的那个点 有可能会更新 上层节点, 所以 每一个环DP完之后,更新 dp[深度最小的点] #include<cstdio> #include<iostream> #include<algorithm> using