CF1503E 2-Coloring
cjx 組合強。
思路
觀察一下題目,不難發現只有當黃色形成如下的單峰時才合法。
(染錯色了,將就一下)
其中兩座峰的峰頂高度相加等於 \(m\),為了方便統計,我們欽定右邊的峰一定在左峰下方的行出現,最後答案乘以二就是最終方案。
發現對於每一邊是兩個最長不下降子序列拼在一起。
設 \(f[i][j]\) 為長度為 \(i\) 的最長不下降子序列,最高點高度為 \(j\) 的方案數。
再設 \(f'[i][j]\) 為長度為 \(i\) 的最長不下降子序列,僅有一個最高點為 \(j\) 的方案數。
答案就是
相當於列舉第一個峰在行 \(i\),第二個峰在行 \(j\),第一個峰高度為 \(k\)。
變幻一下:
這樣給後一個和式做一個字尾和最佳化,即可把複雜度最佳化到 \(O(n^2)\) 級別。
當然有例外是這個樣子。
兩峰交在一起(必須保證在連續的行且峰頂高度相加大於 \(m\)),也是一種方案。
相當於列舉左邊的峰頂在行 \(i\),左峰高度為 \(x\),右峰高度為 \(y\),同時求和了峰過後的一段不下降子序列的方案。
對於 \(\sum_{k=1}^{x-1}f[i][k]\) 和 \(\sum_{k=1}^{y-1} f[n-i][k]\) 字首和最佳化。
然後預處理出所有的
再次字首和。
即可在 \(O(n^2)\) 的時間內求出上述和式。
對於 \(f\) 和 \(f'\) 的轉移是容易的。
初始值 \(f[0][0]=1\)。
其實你看到這裡,已經可以使用 dp 做出本題了,但是如果你想進一步瞭解組合,請移步。
不難發現,我們每次的轉移都相當於求一次字首和,而 \(f[0][0]=1\)。
所以有可以構造這樣的生成函式:
觀察 \(\sum_{i=0} x^i\) 的優雅性質,你會發現 \(1\times \sum_{i=0} x^i\) 相當於求一次字首和。
而 \((\sum_{i=0} x^i)^2\) 相當於求兩次字首和(\(x^i\) 的係數是第 \(i\) 項的求字首和後的值)。
由 \(f[0]\) 轉移到 \(f[1]\) 的過程相當於乘以 \(\sum_{i=0} x^i\)(一次字首和),所以有:
推廣一下,每次 \(f[i]\) 向 \(f[i+1]\) 的轉移都是乘以 \(\sum_{i=0} x^i\)(每轉移一次求一次字首和),所以有:
對於 \(\sum_{i=0} x^i\),的封閉形式為 \((1-x)^{-1}\) 有很多種方法求,這裡不再贅述。
所以有:
使用廣義二項式展開 \((1+(-x))^{-i}\),即可得到第 \(j\) 項的係數為:
關於牛頓二項式係數,已經有完備的公式,本處直接套公式即可,故不再展開討論。
把係數乘上 \((-x)^j\),有
從比較簡單的組合意義考慮,有 \(j\) 個球,有 \(i-1\) 個擋板,可以為空,兩個擋板之間的球數相當於相鄰兩項的差。
那麼有:
在觀察一下 \(f'\) 求值的式子,你會發現 \(f'[i][j]=f[i][j-1]\)。
所以其實你也求出 \(f'\) 的組合意義了。
相當於有 \(j\) 個球,\(i-1\) 個擋板,最後一個擋板至少擋一個球,那麼就可以投入 \(j-1\) 個球供所有擋板擋。
如果考慮生成函式的話,\(f'[i][j]\) 相當於較 \(f[i][j]\) 向後平移了一個位置,那麼可以寫成:
或
於是你就可以 愉快 的 AC 本題了。
CODE
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define N 5000
#define mod 998244353
const int maxn=5e3+5;
ll n,m,ans;
ll fac[maxn],inv[maxn],f[maxn];
inline ll ksm(ll x,ll y)
{
ll sum=1;
for(;y;y/=2,x=x*x%mod) if(y&1) sum=sum*x%mod;
return sum;
}
inline ll C(int n,int m)
{
if(n<m||m<0) return 0;
return 1ll*fac[n]*inv[m]%mod*inv[n-m]%mod;
}
int main()
{
freopen("magic.in","r",stdin);
freopen("magic.out","w",stdout);
for(int i=fac[0]=1;i<=N;i++) fac[i]=fac[i-1]*i%mod;
inv[N]=ksm(fac[N],mod-2);
for(int i=N-1;~i;i--) inv[i]=inv[i+1]*(i+1)%mod;
scanf("%lld%lld",&n,&m);
for(int h=1;h<n;h++)
{
for(int j=m;j>=2;j--)
{
f[j]=(1LL*f[j+1]+1LL*C(j+n-h-2,n-h-1)*C(m-j+n-h,n-h)%mod)%mod;
}
for(int i=m-1;i>=1;i--)
{
ans = (1LL*ans+1LL*C(i+h-1,h)*C(m-i+1+h-2,h-1)%mod*f[i+1]%mod)%mod;
}
}
memset(f,0,sizeof(f));
for(int x=1;x<=m;x++)
{
for(int j=n-1;j>=1;j--)
{
f[j]=(1LL*f[j+1]+1LL*C(m-x+n-j-1,n-j-1)*C(m-x+j-1,j)%mod)%mod;
}
for(int i=1;i<n;i++)
{
ans=(1LL*ans+1LL*C(x+i-1,i)*C(x+n-i-1,n-i-1)%mod*f[n-i+1]%mod)%mod;
}
}
printf("%lld\n",ans*2%mod);
}