1. 程式人生 > >奇怪的背包

奇怪的背包

ret bool line gis 我們 不能 每次 ref ble

奇怪的背包

有一個背包,有一個模數P,有n件物品,第i件物品體積為\(v_i\),物品可以無限取用,最後背包的體積為總體積\(mod\ p\),q組詢問,第i個詢問背包最終體積為\(w_i\)的方案數,兩個方案數的不同不在於物品的放的個數,而在於物品是否取用。

技術分享圖片

顯然物品的取用的次數沒有關系,那麽關鍵在於物品是否取用,以及它能到達的背包體積,於是對於一件物品不難寫出式子

\[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();
}

奇怪的背包