奇怪的背包

奇怪的背包

有一个背包,有一个模数P,有n件物品,第i件物品体积为\(v_i\),物品可以无限取用,最后背包的体积为总体积\(mod\ p\),q组询问,第i个询问背包最终体积为\(w_i\)的方案数,两个方案数的不同不在于物品的放的个数,而在于物品是否取用。

\(n,q\leq 10^6,p\leq 10^9\)

显然物品的取用的次数没有关系,那么关键在于物品是否取用,以及它能到达的背包体积,于是对于一件物品不难写出式子

\[kv_i=z(mod\ p) \]

k为取用的个数,z为得到的背包体积,于是由bezaut定理得知,它能所取用的背包的体积一定为\(gcd(p,v_i)\)的倍数,而同理推广多个数自然为
\(gcd(v_1,v_2,...,v_n,p)\)的倍数。

于是把各个\(gcd(p,v_i)\)维护为\(d[i]\),个数为\(s[j]\),显然只有p的约数个数种,所以不难得知我们应设递推方程\(f[i][j]\)表示选到第i件物品,现在最大公约数为\(d[j]\)的方案数,现在暂时把n改成p的约数个数,所以又不难有

\[f[i][j]=f[i-1][j]+\sum_{k=1}^nf[i-1][k]\times (gcd(d[k],d[i])==d[j])(2^{s[i]-1}) \]

以此可以转移,于是对于询问我们的答案统计为

\[ans=\sum_{j=1}^nf[j][w_i] \]

注意到我们不能每次\(\sqrt{p}\)回答询问,于是我们设法维护出结果,\(O(1)\)回答,显然暴力维护只有p的约数个数平方。

参考代码:

#include <iostream>
#include <cstdio>
#include <algorithm>
#define il inline
#define ri register
#define ll long long
#define yyb 1000000007
#define _ putchar('\n')
#define swap(x,y) x^=y^=x^=y
using namespace std;
ll lsy,d[5000],dt,s[5000],cjx,
    bin[1000001],dp[2][5000],ask[5000];
il void fact(ll);
il int dfs(ll);
il ll gcd(ll,ll);
template<class free>void pen(free);
template<class free>il void read(free&);
int main(){
    int n,q,i,j;bool now(false);
    read(n),read(q),read(lsy),fact(lsy);
    for(i=bin[0]=1;i<=n;++i)read(cjx),++s[dfs(gcd(lsy,cjx))],
                                bin[i]=(bin[i-1]<<1)%yyb;
    for(i=1;i<=dt;++i)s[i]=(bin[s[i]]+yyb-1)%yyb;
    for(i=1;i<=dt;++i,now^=1){
        dp[now][i]=s[i];
        for(j=1;j<=dt;++j)
            dp[now^1][j]=dp[now][j],
                (dp[now^1][dfs(gcd(d[i+1],d[j]))]+=dp[now][j]*s[i+1])%=yyb;
    }
    for(i=1;i<=dt;++i)
        for(j=1;j<=i;++j)
            if(!(d[i]%d[j]))(ask[i]+=dp[now][j])%=yyb;
    while(q--)read(cjx),pen(ask[dfs(gcd(cjx,lsy))]),_;
    return 0;
}
il int dfs(ll x){
    int l(1),r(dt),mid;
    while(l<=r){
        mid=l+r>>1;
        if(d[mid]<x)l=mid+1;
        else r=mid-1;
    }return l;
}
il void fact(ll x){
    ll i;
    for(i=1;i*i<x;++i)
        if(!(x%i))d[++dt]=i,d[++dt]=x/i;
    if(i*i==x)d[++dt]=i;sort(d+1,d+dt+1);
}
template<class free>
void pen(free x){if(x>9)pen(x/10);putchar(x%10+48);}
il ll gcd(ll a,ll b){while(b)swap(a,b),b%=a;return a;}
template<class free>
il void read(free &x){
    x&=0;ri char c;while(c=getchar(),c<'0'||c>'9');
    while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=getchar();
}
posted @ 2019-05-09 17:31  a1b3c7d9  阅读(125)  评论(0编辑  收藏  举报