zoukankan      html  css  js  c++  java
  • [bzoj3462]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<=2*10^6,1<=n<=10^18,1<=q<=10^5

    这个题面让人很费解啊

    其实题意还是挺简洁的

    首先对S分解质因数

    如果有相同的因数出现了多次,那么$lcm(p_1,p_2,p_3...p_k)=S$一定不能被满足

    此时直接特判输出Q个0即可

    (这10分这么好拿还想什么正解hhh)

    接下来要做的,是取一定数量的每个$p_i$,使他们的和为$n$

    假设$n=sum{p_i*c_i}$,即$p_i$对应取$c_i$个

    由$p_i$是S的因数,可得$p_i*c_i$一定可以表示成$X*S+Y*p_i$

    将$p_i$除过去,得到$c_i=X*frac{S}{p_i}+Y$

    那么就可以把$c_i$分成+号前后两部分,不同的$X$和不同的$Y$都会导致方案不同

    设前半部分为$a_i$,后半部分为$b_i$

    将$c_i mod frac{S}{p_i}$得到后半部分,这部分的方案数用多重背包解决

    则背包大小不超过$k*S$(每个物品不超过$frac{S}{p_i}$),

    之后我们考虑$a_i$部分,分配每一个$X$等价于将$frac{S}{p_i}$个物品放到k个盒子内(可空),

    利用隔板法得到方案数为$C_{frac{S}{p_i}+k-1}^{k-1}$

    直接打表求k范围内的阶乘逆元,暴力求排列再$*(m-1)$的阶乘逆元即可

    $a_i b_i$的贡献是相对独立的,所以可以直接相乘

    但这样可能包含了几倍的S,

    而通过枚举一个倍数m( $p_1*b_1+p_2*b_2+...+p_k*b_k=n-m*S$ )即可得到结果

    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<vector>
    using namespace std;
    typedef long long ll;
    const int N=1000005;
    const ll mod=1e9+7;
    int S,Q,tot=0,sumf;
    int fact[9];
    int sz,f[15000001],g[15000001],mu[2050000],pr[2050000];
    bool v[15000001];
    ll n,side,inv[9],fac[9];
    void dp()
    {
        f[0]=1;
        for(int i=1;i<=sz;i++)
        {
            for(int j=0;j<fact[i];j++)
            {
                ll tmp=0;
                for(int k=j;k<=side;k+=fact[i])
                {
                    tmp=(f[k]+tmp)%mod;
                    if(k>=S)tmp=(tmp-f[k-S])%mod;
                    g[k]=tmp;
                }
            }
            for(ll j=0;j<=side;j++)f[j]=g[j];
        }
    }
    ll qpow(ll a,ll b)
    {
        ll res=1;
        for( ;b;b>>=1,a=a*a%mod)
            if(b&1)res=res*a%mod;
        return res;
    }
    ll C(ll x,ll y)
    {
        ll res=1;
        for(ll i=x;i>=x-y+1;i--)res=res%mod*(i%mod)%mod;
        res=res%mod*(inv[y]%mod)%mod;
        return res;
    }
    void prime()
    {
        mu[1]=1;int num=0;
        for(int i=2;i<=S;i++)
        {
            if(!v[i])pr[++num]=i,mu[i]=-1;
            for(int j=1;j<=num;j++)
            {
                if(i*pr[j]>S)break;
                v[i*pr[j]]=1;
                if(i%pr[j])mu[i*pr[j]]=-mu[i];
                else
                {
                    mu[i*pr[j]]=0;
                    break;
                }
            }
        }
        for(int i=1;i<=num;i++)
            if(S%pr[i]==0)fact[++sz]=pr[i],sumf+=pr[i];
    }
    int main()
    {
        scanf("%d%d",&S,&Q);
        prime();
        if(!mu[S])
        {
            while(Q--)puts("0");
            return 0;
        }
        side=S*sz;
        inv[0]=fac[1]=1;
        for(ll i=2;i<=sz;i++)
            fac[i]=fac[i-1]*i%mod;
        inv[sz]=qpow(fac[sz],mod-2);
        for(int i=sz-1;i;i--)
            inv[i]=inv[i+1]*(i+1)%mod;
        dp();
        while(Q--)
        {
            scanf("%lld",&n);
            if(n<sumf)
            {
                puts("0");
                continue ;
            }
            n-=sumf;
            ll tm=n/S,ans=0;
            for(ll i=0;i<=sz;i++)
            {
                if(n-i*S<0)break;
                ll now=i*S+n%S;
                if(now>side)continue;
                ans=(ans+(ll)f[now]*C(tm-i+sz-1,sz-1))%mod;    
            }
            printf("%lld
    ",(ans+mod)%mod);
        }
        return 0;
    }
    View Code
  • 相关阅读:
    RxJava开发精要3-向响应式世界问好
    RxJava开发精要2-为什么是Observables?
    RxJava开发精要1-从.NET到RxJava
    为你的应用加速
    Android最佳性能实践(二)——分析内存的使用情况
    Android最佳性能实践(一)——合理管理内存
    Android 性能优化之使用MAT分析内存泄露问题
    给 Android 开发者的 RxJava 详解
    优化 Android 线程和后台任务开发
    资深谷歌安卓工程师对安卓应用开发的建议
  • 原文地址:https://www.cnblogs.com/Rorschach-XR/p/11123193.html
Copyright © 2011-2022 走看看