• DZY Loves Math II:多重背包dp+组合数学


    Description

    Input

    第一行,两个正整数 S 和 q,q 表示询问数量。
    接下来 q 行,每行一个正整数 n。

    Output

    输出共 q 行,分别为每个询问的答案。

    Sample Input

    30 3
    9
    29
    1000000000000000000

    Sample Output

    0
    9
    450000036

    Hint

    感谢the Loser协助更正数据
    对于100%的数据,2<=S<=2e6​​,1<=n<=101810^{18}1018​​,1<=q<=10510^5105​​

    好题!

    10%算法:

    对于S为平方数的倍数时,输出T个0

    证明:无论用什么素数去lcm,结果都不可能是平方数

    20%算法:

    对于输入的n,因为每个素数至少出现一次(否则lcm凑不到),所以需要改动

    S数据规模可以看出,素数的数量不会超过7个,即背包的物品少于7个

    我们只需要把n减去所有的素数,就相当于先把每个物品都选了一遍

    剩下的就是普通背包,不再讲解。(需要结合上一个10%的算法)

    100%算法:

    n很大,S还可以,物品种数也很少,它们的lcm也在可接受范围之内。

    我颓的唯一一句题解:

    "n很大,我们考虑一种合法方案,每个ci都不小,而p又是s的约数,我们尝试给ci%=s/pi"

    然后就像明白了,受了这个启发后就在自己能力范围之内了

    想一下为什么要模,因为我们可以发现每当取的物品数到达s/pi时,那么总量就到达了s,对于每个物品都是这样

    那么,对于n,可以把它划分成n=as+c,我们把凑满s的部分叫做一个块

    每一个块都可能是被每一个素数凑成的,所以如果有a个块,那么只考虑这些块,总的方案数是多少?

    比较容易想到挡板法,可是不太一样:

    挡板法用于n个相同物品分成k组,每组至少一件,答案是C(n-1,k-1)

    可是我们需要的是n物品k组,每一组至少0件,那么和从背包每个必装一种而使n减掉每种物品相似,我们把物品增加k件从而保证每组都有

    那么就是C(n+k-1,k-1)了

    至于组合数怎么算。。。因为k-1很小,暴力就好,处理出逆元,n+k-1那部分暴力乘

    但是要注意,因为那个部分太大,在乘之前就要取一次模,不然爆LL

    好的我们现在处理完了整块的部分,接下来来考虑零散部分(零散部分已不含整块)

    显然零散部分没有整块的话它的大小不会很大,最多就是素数个数乘以lcm,1.4e7,可以接受,背包解决

    但是,我们怎么保证每个物品都不会凑成一个整块呢?

    那么就有了个数限制,它不再是完全背包,而是多重背包

    怎么办?其实这么想就偏了

    考虑对于dp[m],如果它已经含有整块了,那么去掉这个整块的部分有几种方案,减去不就好了么?

    即dp[m]-=dp[m-s];那么就搞定了个数限制的问题,它还是个完全背包

    1 for(int j=1;j<=cntp;++j){
    2         for(int i=0;i+p[j]<=maxn;++i)
    3             if(bp[i])bp[i+p[j]]=(bp[i]+bp[i+p[j]])%mod;
    4         for(int i=maxn;i>=0;--i)
    5             if(i+ss<=maxn)bp[i+ss]=(bp[i+ss]-bp[i]+mod)%mod;
    6     }
    背包部分代码

    那么就没什么问题了,最后再说一句n=as+c的分解不是简单的取模,c那个部分可以大于s

    因为用k个物品,每个物品的贡献都不到s,但是它们能凑出的最大值接近于ks

    多枚举几轮就好啦

     1 /*
     2 我颓了题解的第一句话,这题我水了
     3 "n很大,我们考虑一种合法方案,每个ci都不小,而p又是s的约数,我们尝试给ci%=s/pi"
     4 然后就不需要在往下看了。
     5 */
     6 #include<cstdio>
     7 #define mod 1000000007
     8 #define int long long
     9 int s,t,p[11],cntp,bp[14000005],maxn,tot,th[202],bj[202],ths,invv[8],inv[8];int n;
    10 inline int max(int a,int b){return a>b?a:b;}
    11 main(){
    12     scanf("%lld%lld",&s,&t);int ss=s;
    13     for(int i=2;i*i<=s;++i)if(s%i==0){
    14         if(s/i%i==0){
    15             for(int i=1;i<=t;++i)puts("0");
    16             return 0;
    17         }
    18         p[++cntp]=i;s/=i;tot+=i;
    19     }
    20     if(s!=1)p[++cntp]=s,tot+=s;maxn=cntp*ss;
    21     bp[0]=1;
    22     for(int j=1;j<=cntp;++j){
    23         for(int i=0;i+p[j]<=maxn;++i)
    24             if(bp[i])bp[i+p[j]]=(bp[i]+bp[i+p[j]])%mod;
    25         for(int i=maxn;i>=0;--i)
    26             if(i+ss<=maxn)bp[i+ss]=(bp[i+ss]-bp[i]+mod)%mod;
    27     }
    28     inv[0]=invv[1]=inv[1]=1;for(int i=2;i<=7;++i)invv[i]=(mod+(-(mod/i)*invv[mod%i]%mod))%mod,inv[i]=inv[i-1]*invv[i]%mod;
    29     while(t--){
    30         scanf("%lld",&n);n-=tot;
    31         if(n<0){puts("0");continue;}
    32         if(n<ss){printf("%lld
    ",bp[n]);continue;}
    33         int tms=n/ss,ans=0;
    34         for(int i=max(0,tms-cntp+1);i<=tms;++i){
    35             int tans=inv[cntp-1]*bp[n-i*ss]%mod;
    36             for(int j=0;j<cntp-1;++j)tans=tans*((i-1+cntp-j)%mod)%mod;
    37             ans=(ans+tans)%mod;
    38         }
    39         printf("%lld
    ",ans);
    40     }
    41 }
    Finally Accept
  • 相关阅读:
    【SVN解决代码提交冲突】https://www.cnblogs.com/aaronLinux/p/5521844.html
    查询有2门及以上不及格科目的学生姓名及其平均成绩
    【Python】split
    【Python】文件处理
    【robotframework】打开浏览器提示:NoSuchWindowException: Message: Unable to get browser
    定位到新窗口
    8月1号
    【定位】https://blog.csdn.net/cyjs1988/article/details/76284289
    【Robotframework】脚本跑完后自动发送邮件
    jQuery Mobile Data 属性
  • 原文地址:https://www.cnblogs.com/hzoi-DeepinC/p/11131047.html
Copyright © 2020-2023  润新知