• [省选联考2022]卡牌


    呆滞,卡了一天的常发现 \(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";
    	}
    }
    
  • 相关阅读:
    Construct Binary Tree From Inorder and Postorder Traversal
    Construct Binary Tree From Preorder and Inorder Traversal **
    Populating Next Right Pointers in Each Node II
    Populating Next Right Pointers in Each Node
    Flatten Binary Tree to Linked List
    E1总结和CISCO E1配置
    Dial-peer匹配顺序
    CUCM来实现PLAR
    CUCM实现Extension Mobility
    语音笔记20 URI
  • 原文地址:https://www.cnblogs.com/dixiao/p/16167235.html
Copyright © 2020-2023  润新知