Codeforces 1257G Divisor Set Dilworth定理+分治FFT

题意

给你一个大数由n个可重复的质数相乘,要求求出一个这个大数的约数的子集,使得这个子集里面的数两两互补为对方的倍数
n 2 1 0 5 n\leq2*10^5

分析

考虑这样的子集是什么样子的

把一个数向所有的约数连一条有向边,然后就是等于求最长反链(即选出最多的点两两不到达)
又由于最长反链 = 最小链覆盖(即选出最少的链覆盖所有的点,每个点至少覆盖一次)

发现这样的图是以质数的幂次 k k 来分层的,而且只有不同层之间连边,我们只需要找到点数最大的层

现在问题就变成了每个质数都有 c n t i cnt_i 个,选出 0 x i c n t i 0 \leq x_i \leq cnt_i 个,使得 Σ x i = k \Sigma x_i = k 的方案数

这可以用生成函数来解决,并且因为系数都是1,乘完之后的生成函数是中间凸两边低的(并且是对称的)
两个这样的生成函数乘起来还是符合这个性质,所以最中间的系数是最大的,即 k = n 2 k=\lceil{\frac{n}{2}}\rceil 时最大

代码

#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;
}

猜你喜欢

转载自blog.csdn.net/weixin_39708759/article/details/103264687