题意
给你一个大数由n个可重复的质数相乘,要求求出一个这个大数的约数的子集,使得这个子集里面的数两两互补为对方的倍数
n
≤
2
∗
1
0
5
n\leq2*10^5
n≤2∗105
分析
考虑这样的子集是什么样子的
把一个数向所有的约数连一条有向边,然后就是等于求最长反链(即选出最多的点两两不到达)
又由于最长反链 = 最小链覆盖(即选出最少的链覆盖所有的点,每个点至少覆盖一次)
发现这样的图是以质数的幂次 k k k来分层的,而且只有不同层之间连边,我们只需要找到点数最大的层
现在问题就变成了每个质数都有 c n t i cnt_i cnti个,选出 0 ≤ x i ≤ c n t i 0 \leq x_i \leq cnt_i 0≤xi≤cnti 个,使得 Σ x i = k \Sigma x_i = k Σxi=k的方案数
这可以用生成函数来解决,并且因为系数都是1,乘完之后的生成函数是中间凸两边低的(并且是对称的)
两个这样的生成函数乘起来还是符合这个性质,所以最中间的系数是最大的,即
k
=
⌈
n
2
⌉
k=\lceil{\frac{n}{2}}\rceil
k=⌈2n⌉时最大
代码
#include <bits/stdc++.h>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/assoc_container.hpp>
#define pb push_back
#define mp make_pair
#define fi first
#define se second
using namespace __gnu_pbds;
using namespace std;
typedef long long ll;
typedef pair<int,int> pii;
typedef tree<int,null_type,less<int>,rb_tree_tag,tree_order_statistics_node_update> splay;
const int N = 200010;
const int mod = 998244353;
inline int read()
{
int p=0; int f=1; char ch=getchar();
while(ch<'0' || ch>'9'){if(ch=='-') f=-1; ch=getchar();}
while(ch>='0' && ch<='9'){p=p*10+ch-'0'; ch=getchar();}
return p*f;
}
int qpow(int x,int k,int mo)
{
int s = 1;
while(k)
{
if(k&1) s=(ll) s*x%mo;
x=(ll) x*x%mo; k>>=1;
}return s;
}
int r[N<<2];
void dft(int *a,int n,int op)
{
for(int i=0;i<n;i++) if(r[i] > i) swap(a[r[i]] , a[i]);
for(int i=1;i<n;i<<=1)
{
int gn = qpow(3ll,(mod-1) / (i<<1), mod);
for(int j=0;j<n;j+=(i<<1))
{
int g = 1;
for(int k=0;k<i;k++,g=(ll) g*gn%mod)
{
int x = a[j+k]; int y = (ll) a[j+k+i] * g % mod;
a[j+k] = (x+y)%mod;
a[j+k+i] = (x-y+mod)%mod;
}
}
}
if(op==-1) reverse(a+1,a+n);
}
int A[N<<2],B[N<<2],C[N<<2];
struct node
{
vector<int> a;
node(){}
friend node operator * (node x,node y)
{
int m = x.a.size() + y.a.size() - 2; int n,l=0;
for(n=1;n<=m;n<<=1) l++;
for(int i=0;i<n;i++) A[i] = B[i] = C[i] = 0;
for(int i=0;i<x.a.size();i++) A[i] = x.a[i];
for(int i=0;i<y.a.size();i++) B[i] = y.a[i];
r[0] = 0; for(int i=1;i<n;i++) r[i] = (r[i>>1] >> 1) | ((i&1) << (l-1));
dft(A,n,1); dft(B,n,1);
for(int i=0;i<n;i++) C[i] = (ll) A[i] * B[i] % mod;
dft(C,n,-1); int invn = qpow(n,mod-2,mod);
for(int i=0;i<n;i++) C[i] = (ll) C[i] * invn % mod;
node z; for(int i=0;i<=m;i++) z.a.pb(C[i]); return z;
}
}p[400010]; int tot = 0;
int cnt[3000010];
priority_queue<pii>q;
int d[N];
int main()
{
// freopen("a.in","r",stdin);
int n = read(); while(!q.empty()) q.pop();
for(int i=1;i<=n;i++) d[i] = read(),cnt[d[i]]++;
for(int i=1;i<=3000000;i++) {
if(cnt[i]) {
tot ++;
for(int j=0;j<=cnt[i];j++) p[tot].a.pb(1);
q.push(mp(-cnt[i]-1,tot));
}
}
while(q.size() > 1) {
pii x = q.top(); q.pop();
pii y = q.top(); q.pop();
p[x.se] = p[x.se] * p[y.se]; p[y.se].a.clear();
q.push(mp(-p[x.se].a.size(),x.se));
}
return printf("%d\n",p[q.top().se].a[(n+1)/2]),0;
}