1 条题解

  • 0
    @ 2025-8-24 22:00:45

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar Bruteforces
    **

    搬运于2025-08-24 22:00:45,当前版本为作者最后更新于2018-07-09 14:33:26,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    题解

    我们首先考虑 n=1n=1 的情况,通过打表可以发现,对于体积为 vv 的物品,在可以取无限次的条件下,能够取到的体积对 PP 取膜的值可以为

    gcd(v,P),2gcd(v,P),3gcd(v,P)...gcd(v,P),2*gcd(v,P),3*gcd(v,P)...

    所以我们可以把这个物品的体积看做 gcd(v,P)gcd(v,P),这对答案不会有任何影响。

    由此我们得到启发,O(P)O(\sqrt{P}) 预处理出 PP 的每个约数,存储每个约数出现的次数。由于同一个约数多次选取对答案没有任何影响,容易得出,如果 PP 的一个约数出现次数为 xx,则有 2x12^x-1 种方案选取这个约数。

    继续打表我们又可以发现,如果选取了 PP 的两个约数 a1,a2a_{1},a_{2},能够取到的值膜 PP 的值可以为

    $gcd(a_{1},a_{2}),2*gcd(a_{1},a_{2}),3*gcd(a_{1},a_{2})...$

    我们可以口胡证明,当选取约数个数大于 2 时,上式依然成立。

    由此我们可以写出DP方程,设 F[i][j]F[i][j] 表示选到 PP 的第 ii 个约数,选出的数的 gcd 值为 PP 的第 jj 个约数的方案数,则转移方程为:

    $F[i][j]=F[i-1][j]+(1+\sum{_{gcd(a[k],a[i])==a[j]}F[i-1][k]})*(2^{s[i]}-1)$

    其中 a[i]a[i] 表示 PP 的第 ii 个约数,s[i]s[i] 表示第 ii 个约数的出现次数。

    该部分时间复杂度为 O(M2logM)O(M^2logM),空间复杂度为 O(M2)O(M^2)O(2M)O(2M) (滚动数组),其中 MMPP 的约数个数。

    这个时候,对于每一个询问 wiw_{i},我们容易得出答案就是

    a[i]wiF[n][i]\sum{_{a[i]|w_{i}}F[n][i]}

    但是询问数量可以达到 10610^6MM 最大可以达到 10310^3 以上,O(qM)O(qM) 的暴力统计依然会TLE。

    事实上我们可以发现,如果一个数既是 PP 的约数,又是 wiw_{i} 的约数,那么它一定是 gcd(P,wi)gcd(P,w_{i}) 的约数,因此我们只需要统计

    a[i]gcd(P,wi)F[n][i]\sum{_{a[i]|gcd(P,w_{i})}F[n][i]}

    而这可以在DP之后就预处理出来:

    G[i]=a[j]a[i]F[n][j]G[i]=\sum{_{a[j]|a[i]}F[n][j]}

    因此我们可以 O(1)O(1) 回答询问,这样总复杂度就是 O(P+M2logM+q)O(\sqrt{P}+M^2logM+q),这道题就可以AC啦

    代码如下:

    #include<bits/stdc++.h>
    using namespace std;
    
    const int N=1000010,M=10050,mod=1000000007;
    int n,q,P,v[N],num[M],tot[M],cnt=0,f[2][M],g[M],now=0,sum[N];
    
    inline void init(){
        sum[1]=2;
        for(register int i=2;i<=n;i++)sum[i]=sum[i-1]*2%mod;
        for(register int i=1;i<=n;i++)sum[i]=(sum[i]+mod-1)%mod;
        
        for(register int i=1;i<=sqrt(P);i++)if(P%i==0)num[++cnt]=i;
        for(register int i=cnt;i>1;i--)if(P/num[i]!=num[i])num[++cnt]=P/num[i];
        for(register int i=1;i<=n;i++){
            int pos=lower_bound(num+1,num+cnt+1,v[i])-num;
            tot[pos]++;
        }
        
        for(register int i=1;i<=cnt;i++)if(tot[i]){
            now^=1;
            for(register int j=1;j<=cnt;j++)f[now][j]=f[now^1][j];
            for(register int j=1;j<=cnt;j++)if(f[now^1][j]){
                int nxt=__gcd(num[j],num[i]);
                int pos=lower_bound(num+1,num+cnt+1,nxt)-num;
                (f[now][pos]+=1LL*f[now^1][j]*sum[tot[i]]%mod)%=mod;
            }
            (f[now][i]+=sum[tot[i]])%=mod;
        }
        for(register int i=1;i<=cnt;i++){
        	for(register int j=1;j<=i;j++)if(num[i]%num[j]==0){
                (g[i]+=f[now][j])%=mod;
            }
        }
    }
    
    int main(){
        scanf("%d%d%d",&n,&q,&P);
        for(register int i=1;i<=n;i++)scanf("%d",&v[i]),v[i]=__gcd(v[i],P);
        init();
        for(register int i=1;i<=q;i++){
            int x,ans=0;scanf("%d",&x);x=__gcd(x,P);
            int pos=lower_bound(num+1,num+cnt+1,x)-num;
            printf("%d\n",g[pos]);
        }
        return 0;
    }
    
    • 1

    信息

    ID
    3480
    时间
    2000ms
    内存
    500MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者