[省選聯考2022]卡牌
阿新 • • 發佈:2022-04-19
呆滯,卡了一天的常發現 \(umap\) 部分要跑 \(3s\) 多,被演了。
考慮壽司晚宴的熟悉套路:
大於根號的質數因子最多隻有一個。
我們考慮按其分類:
並對每一個大質數類,都把內的數字按:
\(a_0 = 1,a_{now} = 2^{cnt} - 1\)
其中\(now\)對應小質數的位置集合。
這樣實際上最終的答案即為這些物品的\(or\)揹包的答案。
考慮先對每個大質數類的物品都\(FMT\)乘起來。
先求出全域性無限制答案。當我們強制大質數\(x\)要被選時:實際上是其對應的揹包結果的\(a_0 - 1\),對應到\(FMT\)陣列上即全域性\(-1\).
那麼只要對大質數類的結果求出\(\frac{F_i - 1}{F_i}\)
考慮特判\(43*43\),將\(43\)也歸為大質數
值得注意是卡常時可以將\(FMT\)的過程手動正向展開少一個\(log\)(因為初陣列只有兩個元素)。
這樣可以做到複雜度 \(O((2000 + \sum c_i)\times 2^{13} + m2^{13}\times 13 + (C) * 2^{} \times log mod)\)
其中\(C\)是大質數數量。
點選檢視程式碼
//晦暗的宇宙,我們找不到光,看不見盡頭,但我們永遠都不會被黑色打倒。——Quinn葵因 #include<bits/stdc++.h> #define ll long long #define N 4005 #define mod 998244353 #define LIM 43 int vis[N]; using std::vector; vector<int>P; int w[N],t[N]; int tcnt; int F[N][(1ll << 14)]; int G[N][(1ll << 14)]; int S[(1ll << 14)]; int R[(1ll << 14)]; int cnt[N]; inline void FWT_or(int *f,int type){ int n = (1ll << 13); for(int mid = 1;mid < n;mid <<= 1) for(int block = mid << 1,j = 0;j < n;j += block) for(int i = j;i < j + mid;++i) f[i + mid] = (f[i + mid] + f[i] * type + mod) % mod; } inline void FWT_and(int *f,int type){ int n = (1ll << 13); for(int mid = 1;mid < n;mid <<= 1) for(int block = mid << 1,j = 0;j < n;j += block) for(int i = j;i < j + mid;++i) f[i] = (f[i] + f[i + mid] * type + mod) % mod; } using std::unordered_map; int INV[N][(1ll << 13)]; inline ll qpow(ll a,ll b){ ll res = 1; while(b){ if(b & 1)res = res * a % mod; b >>= 1; a = a * a % mod; } return res; } inline void print(int x){for(int i = 1;i <= 13;++i)std::cout<<((x >> (i - 1) & 1))<<" ";} #define M 2000 inline void init(){ for(int i = 2;i <= 2000;++i){ if(!vis[i])P.push_back(i); for(int j = 2;j * i <= 2000;++j) vis[i * j] = 1; } for(int i = 1;i <= M;++i)for(int j = 0;j < (1ll << 13);++j)F[i][j] = 1; for(auto v : P)if(v < LIM)w[v] = tcnt++; for(int i = 1;i <= M;++i){ if(cnt[i]){ int now = 0; int res = i; for(auto v : P){ if(v < LIM && res % v == 0){now = now | (1ll << w[v]);res /= v;} while(v < LIM && res % v == 0)res /= v; } for(int j = 0;j < (1ll << 13);++j)F[i][j] = 0; if(res > 1)t[i] = res; if(res == 43 * 43)t[i] = 43; int r = (qpow(2,cnt[i]) - 1) % mod; for(int j = 0;j < (1ll << 13);++j){ F[i][j] = 1; if((j & now) == now) F[i][j] = (F[i][j] + r) % mod; } } } for(int i = 0;i < (1ll << 13);++i)S[i] = 1; for(int i = 1;i <= M;++i) for(int j = 0;j < (1ll << 13);++j) S[j] = (1ll * S[j] * F[i][j]) % mod; for(int i = 1;i <= M;++i)for(int j = 0;j < (1ll << 13);++j)G[i][j] = 1; for(int i = 1;i <= M;++i){ if(t[i]){ for(int j = 0;j < (1ll << 13);++j) G[t[i]][j] = (1ll * G[t[i]][j] * F[i][j]) % mod; } } } int n,m; using std::vector; vector<int>p,d; inline int read(){int x;scanf("%d",&x);return x;} signed main(){ freopen("card.in","r",stdin); freopen("card.out","w",stdout); scanf("%d",&n); for(int i = 1;i <= n;++i){int x;scanf("%d",&x);cnt[x] ++ ;} init();scanf("%d",&m); while(m -- ){ int q;scanf("%d",&q); p.clear();d.clear(); for(int i = 1;i <= q;++i)p.push_back(read()); std::sort(p.begin(),p.end());p.erase(std::unique(p.begin(),p.end()),p.end()); ll now = 0; for(int i = 0;i < (1ll << 13);++i)R[i] = S[i]; for(auto v : p){ if(v < LIM)now = (now) | (1ll << w[v]); else d.push_back(v); } for(int i = 0;i < (1ll << 13);++i){ R[i] = S[i]; for(auto v : d){ if(!INV[v][i])INV[v][i] = 1ll * (G[v][i] - 1) * qpow(G[v][i],mod - 2) % mod; R[i] = 1ll * R[i] * INV[v][i] % mod; } } ll res = 0; FWT_or(R,-1); for(int i = 0;i < (1ll << 13);++i) if((now & i) == now){ res = (res + R[i]); if(res > mod) res -= mod; } std::cout<<res<<"\n"; } }