奇怪的背包
阿新 • • 發佈:2019-05-09
ret bool line gis 我們 不能 每次 ref ble 的倍數。
回答,顯然暴力維護只有p的約數個數平方。
奇怪的背包
有一個背包,有一個模數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)\)
參考代碼:
#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(); }
奇怪的背包