[USACO20OPEN] Exercise P

zzafanti發表於2024-08-02

有意思的計數題。

題目連結

題意

求所有長度為 \(n\) 的排列的所有環長的 \(\text{lcm}\) 的乘積。

  • \(n\leq 7500\)

解法

先 min-max 容斥把 \(\text{lcm}\) 換成 \(\gcd\)

\(\prod\limits_{\sigma} \prod \limits_{T\neq \emptyset} \gcd(T)^{(-1)^{|T|}}\),其中 \(T\) 表示環的大小的(可重)集合。

莫反,得到式子 \(\prod\limits_{d=1}^n d^{\sum\limits_{p} \mu(p) \sum\limits_{\sigma} \sum\limits_{T\neq \emptyset} [\forall i,(d\times p)\mid T_i] (-1)^{|T|}}\)

現在要對 \(d,p\) 求出所有排列的滿足每個元素是 \(d\times p\) 的倍數的環長集合的帶符號的和。

樸素的 dp 可以設計狀態 \(f_{i,j,k,u}\) 表示考慮長度為 \(i\) 的排列中,大小為 \(j\) 且其中環長都是 \(k\) 的倍數且所有環長和為 \(u\) 的集合的數量。

這個計數是無標號的,需要每次轉移欽定當前選的環在序列中最靠右的位置是當前所有環上的位置中最靠右的。

由於要容斥,\(j\) 那一維只記奇偶即可

但是複雜度仍然是很高的,除了暴力分啥都跑不了。

考慮精簡一下狀態。

我們發現,只需要知道對於長度為 \(u\) 的排列,每個環是 \(k\) 的倍數,且總長度是 \(u\) 的環的集合的數量,放在長度為 \(n\) 的排列裡的可以乘上組合數,並且要求剩下 \(n-u\) 個數亂排即可。

同樣地,轉移的時候需要欽定當前加入的環的最右端點在當前排列的最後,滿足無標號。

狀態數就是 \(O(\sum \frac{n}{k})=O(n\ln n)\) 的。

轉移需要 \(O(\frac{n}{k})\) 的複雜度,總的複雜度是 \(O(\sum \frac{n^2}{k^2})=O(n^2)\) 的。

最後統計答案也容易做到 \(O(\sum \frac{n^2}{k^2})=O(n^2)\)

需要注意,我們 dp 的值在指數上,所以要模 \(\text{mod}-1\)

參考程式碼

#include<bits/stdc++.h>

using namespace std;
using E=long long;
E mod,mods;
const int N=7555;

E ksm(E a,E b){
  E ret=1;
  b=(b%mods+mods)%mods;
  while(b){
    if(b&1) ret=ret*a%mod;
    a=a*a%mod;
    b>>=1;
  }
  return ret;
}

int n;

vector<int> dp[N][2];
int C[N][N];
E fac[N];
bitset<N> st;
vector<int> pr,mu;

int main(){

  cin>>n>>mod; mods=mod-1;
  mu.resize(n+1);

  fac[0]=1;
  for(int i=1; i<=n; i++) fac[i]=fac[i-1]*i%mods;

  C[0][0]=1;
  for(int i=1; i<=n; i++){
    for(int j=0; j<=i; j++){
      C[i][j]=C[i-1][j];
      if(j) C[i][j]=(C[i][j]+C[i-1][j-1])%mods;
    }
  }

  mu[1]=1;
  for(int i=2; i<=n; i++){
    if(!st[i]){
      pr.emplace_back(i);
      mu[i]=-1;
    }
    for(auto p:pr){
      if(p*i>n) break;
      st[p*i]=1;
      if(i%p==0){
        mu[i*p]=0;
        break;
      }
      mu[i*p]=mu[i]*mu[p];
    }
  }

  for(int t=1; t<=n; t++){
    for(int j:{0,1}) dp[t][j].resize(n/t+10);
    dp[t][0][0]=1;
    for(int i=1; i*t<=n; i++){
      for(int k=1; k<=i; k++){
        for(int j=0; j<=1; j++){
          dp[t][j][i]=(dp[t][j][i]+dp[t][j^1][i-k]*1ll*C[i*t-1][k*t-1]%mods*fac[k*t-1])%mods;
        }
      }
    }
  }

  E ans=1;
  for(E d=1; d<=n; d++){
    E x=0;
    for(E p=1; p*d<=n; p++){
      for(int sum=1; sum*p*d<=n; sum++){
        x=(x+mu[p]*(dp[p*d][1][sum]*1ll-dp[p*d][0][sum]+mods)%mods*C[n][sum*p*d]%mods*fac[n-sum*p*d])%mods;
      }
    }
    //cerr<<x<<endl;
    ans=ans*ksm(d,x)%mod;
  }

  cout<<ans;

  return 0;
}

相關文章