peiwenjun's blog 没有知识的荒原

CF756E Byteland coins 题解

题目描述

\(n\) 种货币,第 \(1\) 种货币面值为 \(1\) ,第 \(k+1\) 种货币的面值是第 \(k\) 种的 \(a_k\) 倍。

保证 \(\forall x\) ,面值为 \(x\) 的货币种类不超过 \(20\)

\(k\) 种货币有 \(b_k\) 个,求有多少种组成面值 \(m\) 的方案,对 \(10^9+7\) 取模。

两种方案不同,当且仅当至少一种货币出现次数不同。

数据范围

  • \(1\le n\le 3\cdot 10^5,0\le m\le 10^{10000}\)
  • \(1\le a_i\le 10^9,0\le b_i\le 3\cdot 10^5,\sum b_i\le 3\cdot 10^5\)

时间限制 \(\texttt{1s}\) ,空间限制 \(\texttt{512MB}\)

分析

先考虑 \(a_i\neq 1\) 的情况,容易想到数位 \(\texttt{dp}\)

\(m\) 写成变进制表示,记 \(m=\sum\limits_{i=1}^nc_i\cdot A_i\) ,其中 \(0\le c_i<a_i\)\(A_i=\prod_{j=1}^{i-1}a_j\) 为第 \(i\) 种货币的面值。

\(f_{i,j}\) 表示已经确定第 \(i\sim n\) 种货币的取法,还需要 \(j\cdot A_k\) 的面值的方案数。

先从第 \(i+1\) 层转移到第 \(i\) 层: \(f_{i,a_i\cdot j+c_i}\gets f_{i+1,j}\)

再考虑第 \(i\) 种货币选几个, \(f'_{i,j}=\sum_{k=0}^{b_i}f_{i,j+k}\) ,前缀和优化可以做到 \(\mathcal O(1)\) 转移。


接下来的问题是第二维 \(j\) 应该开多大。

如果 \(\sum_{k=1}^ib_kA_k<j\cdot A_i\) ,换言之取完后面所有货币都不足以填补漏洞,那么这个状态没有意义。

所以对于第 \(i\) 层,第二维开到 \(mx_i=\lfloor\frac{\sum_{k=1}^ib_kA_k}{A_i}\rfloor\) 即可,递推式 \(mx_{i+1}=\lfloor\frac{mx_i}{a_i}\rfloor+b_{i+1}\)

考虑单个 \(b_i\) 对状态总数 \(\sum mx_i\) 的贡献,它对 \(mx_i\) 贡献了 \(b_i\) ,对 \(mx_{i+1}\) 贡献不超过 \(\lfloor\frac{b_i}2\rfloor\) ,每升一层贡献至少减半。

因此 \(\sum mx_i\le\sum(b_i+\frac{b_i}2+\frac{b_i}4+\cdots)=2\sum b_i\) ,可以接受。

时间复杂度 \(\mathcal O(\log^2m+\sum b_i)\) ,前者为计算 \(c_k\) 的时间复杂度。


再考虑存在 \(a_i=1\) 的情况,转移方程同上。

由于每种面值至多 \(20\) 种,所以每升 \(20\) 层贡献至少减半,状态总数 \(\le 2\cdot 20\cdot\sum b_i\le 1.2\cdot 10^7\)

预处理 \(c_k\) 的代价有点高,考虑压位高精。以 \(10^9\) 进制存储 \(m\) ,每次除以 \(a_k\) ,则余数为 \(c_k\)

时间复杂度 \(\mathcal O\big((\frac{\log m}9)^2+40\sum b_i\big)\)

#include<bits/stdc++.h>
using namespace std;
const int maxn=5e5+5,mod=1e9+7;
int l,n;
int a[maxn],b[maxn],c[maxn],mx[maxn];
char ch[maxn];
int pw[10],t[maxn];
int f[2][maxn],s[maxn];
int sum(int l,int r)
{
    return l<=0?s[r]:(s[r]-s[l-1]+mod)%mod;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n-1;i++) scanf("%d",&a[i]);
    for(int i=1;i<=n;i++) scanf("%d",&b[i]);
    scanf("%s",ch),l=strlen(ch),reverse(ch,ch+l);
    pw[0]=1;
    for(int i=1;i<=9;i++) pw[i]=10*pw[i-1];
    for(int i=0;i<l;i++) t[i/9]+=(ch[i]-'0')*pw[i%9];
    l/=9;
    for(int i=1;i<=n-1;i++)
    {
        if(a[i]==1) continue;
        int cur=0;
        for(int j=l;j>=0;j--)
        {
            long long tmp=t[j]+1ll*pw[9]*cur;
            t[j]=tmp/a[i],cur=tmp%a[i];
        }
        c[i]=cur;
        while(l>=0&&!t[l]) l--;
    }
    mx[1]=b[1];
    for(int i=2;i<=n;i++) mx[i]=mx[i-1]/a[i-1]+b[i];
    if(l>=1) printf("0\n"),exit(0);
    c[n]=t[0],f[(n+1)&1][0]=1;
    for(int i=n;i>=1;i--)
    {
        for(int j=0;j<=mx[i];j++) f[i&1][j]=0;
        for(int j=0;j<=mx[i+1];j++) if(1ll*a[i]*j+c[i]<=mx[i]) f[i&1][a[i]*j+c[i]]=f[(i+1)&1][j];
        s[0]=f[i&1][0];
        for(int j=1;j<=mx[i];j++) s[j]=(s[j-1]+f[i&1][j])%mod;
        for(int j=0;j<=mx[i];j++) f[i&1][j]=(s[min(j+b[i],mx[i])]-(j>=1?s[j-1]:0)+mod)%mod;
    }
    printf("%d\n",f[1][0]);
    return 0;
}

posted on 2023-02-03 19:24  peiwenjun  阅读(11)  评论(0)    收藏  举报

导航