題面
$\quad $ 我們記 \(F(x)\) 為 \(x\) 為真的方案數,\(len\) 為序列最長連續相同子段長度。
$\quad $ 那麼就有:
$\quad $ 也就是:
$\quad $ 這裡可以畫個圖,發現結果形如三角形,即可得出上式。再改變一下形式:
$\quad $ 然後考慮如何求解 \(F(len<=i)\) ,我們列舉這個序列分為 \(j\) 個區間,這些區間中元素的顏色一致且相鄰區間顏色不同。可以發現,這樣覆蓋了全部的情況。
$\quad $ 發現所有區間的長度都小於等於 \(i\) 比較難做,沒有限制的直接插空即可解決,所以我們考慮容斥,列舉這 \(j\) 個區間裡有 \(k\) 個是長度大於 \(i\) 的,然後對於每一個列舉出來的 \(k\) ,我們將這幾個區間長度減去 \(i\) ,那麼這個問題就可以拿插空做了。於是得出式子:
$\quad $ 我們更換列舉順序:
\begin{aligned}
ans&=nm ^{n} -\sum _{i=1}^{n-1}\sum _{k=0}^{n}(-1) ^{k}m\sum _{j=k}^{n}(m-1) ^{j-1}C _{j}^{k} C _{n-ik-1}^{j-1}\\
&=nm ^{n} -\sum _{i=1}^{n-1}\sum _{k=0}^{n}(-1) ^{k}m(n-ik) ^{-1}\sum _{j=k}^{n}(m-1) ^{j-1}C _{j}^{k} C _{n-ik}^{j}j
\end{aligned}
$\quad $ 現在看最後那個和式的組合意義。
$\quad $ 我們在 \(n-ik\) 個數中先選出 \(j\) 個數,再在這 \(j\) 個數中選出 \(k\) 個數,然後在 \(j\) 中選出一個數(下文記作 \(x\) )不染色,並對剩下的 \(j-1\) 個數染色,(這裡的染色就是確定每個元素的種類,並且每個數只有 \(m-1\) 種可能)。
$\quad $ 然後我們可以先選出這 \(k\) 個數,然後再選出 \(x\)(注意 \(x\) 可以在那 \(k\) 個數中),然後去找能夠包含他們的、大小為 \(j\) 的集合,並對集合中剩下的元素染色。
$\quad $ 對於現已經選擇的數之外的數,他可以是選或不選,如果選了就有 \(m-1\) 種可能,那麼就可以直接看做是給這些數染色,不過這裡的每個元素有 \(m\) 種可能。
$\quad $ 那麼這個時候 \(x\) 的位置就很重要了,這裡就分出兩種情況, \(x\) 在選出的 \(k\) 個數裡、\(x\) 不在那 \(k\) 個數裡。
$\quad $ 當 \(x\) 在那 \(k\) 個數中時,答案就是 \(C _{k}^{1}(m-1) ^{k-1}m ^{n-ik}\) ,也就是先確定出 \(x\) 的可能,然後對剩下的 \(k-1\) 個元素染色,再對這 \(k\) 個數外的的元素染色。注意這兩種元素的染色可能數並不相同,見上文。
$\quad $ 當 \(x\) 不在那 \(k\) 個數裡時,答案就是 \(C _{n-ik}^{1}(m-1) ^{n-ik-1} m ^{k}\) 。原理和上一種情況一樣。
$\quad $ 那麼我們就得出了最終的答案:
點選檢視程式碼
#define yhl 0
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N=3e5+10;
int n,m,p,ans,fmo[N],fm[N],fact[N],ny[N],iv[N];
inline int calc(int i,int k){return (k*fmo[k-1]%p*fm[n-i*k-k]%p+fmo[k]*(n-i*k-k)%p*fm[n-i*k-k-1]%p)%p;}
inline int qum(int a,int b){
int ans=1;
while(b){
(b&1)&&(ans=ans*a%p);
a=a*a%p;
b>>=1;
}
return ans;
}
inline int C(int n,int m){return fact[n]*ny[n-m]%p*ny[m]%p;}
signed main(){
scanf("%lld%lld%lld",&n,&m,&p);
fmo[yhl]=fm[yhl]=fact[yhl]=ny[yhl]=1;
for(int i=1;i<=n;i++){
fmo[i]=fmo[i-1]*(m-1)%p;
fm[i]=fm[i-1]*m%p;
fact[i]=fact[i-1]*i%p;
}
ny[n]=qum(fact[n],p-2);
for(int i=n-1;i;i--)ny[i]=ny[i+1]*(i+1)%p;
for(int i=1;i<=n;i++)iv[i]=fact[i-1]*ny[i]%p;
for(int i=1;i<n;i++){
for(int k=yhl;k<=n/(i+1);k++){
ans=(ans+(-1*(k&1)+1*((k&1)^1))*iv[n-i*k]%p*C(n-i*k,k)%p*calc(i,k)%p+p)%p;
}
}
ans=(n*qum(m,n)%p-m*ans%p+p)%p;
printf("%lld",ans);
return yhl;
}
後記
$\quad $ 開始推式子的時候看 \(C _{j}^{k} C _{n-ik}^{j}\) 不是很順眼,然後就給它展開湊項變成了 \(C _{n-ik}^{k}C _{n-ik-k}^{j-k}\) ,然後發現這個東西是可以推廣的,也就是:
$\quad $ 證明:
$\quad $ 然後以為自己發現了什麼了不得的東西,但是一想自己沒那麼厲害,怎麼可能發現了前人沒發現的東西,搜了一下發現在《具體數學》裡提及了。