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

題意

給你一個大數由n個可重複的質數相乘,要求求出一個這個大數的約數的子集,使得這個子集裏面的數兩兩互補爲對方的倍數
n2105n\leq2*10^5

分析

考慮這樣的子集是什麼樣子的

把一個數向所有的約數連一條有向邊,然後就是等於求最長反鏈(即選出最多的點兩兩不到達)
又由於最長反鏈 = 最小鏈覆蓋(即選出最少的鏈覆蓋所有的點,每個點至少覆蓋一次)

發現這樣的圖是以質數的冪次kk來分層的,而且只有不同層之間連邊,我們只需要找到點數最大的層

現在問題就變成了每個質數都有cnticnt_i個,選出0xicnti0 \leq x_i \leq cnt_i 個,使得Σxi=k\Sigma x_i = k的方案數

這可以用生成函數來解決,並且因爲係數都是1,乘完之後的生成函數是中間凸兩邊低的(並且是對稱的)
兩個這樣的生成函數乘起來還是符合這個性質,所以最中間的係數是最大的,即k=n2k=\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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章