感覺這題非常厲害
爲了方便解釋,做以下約定
將全序列記爲
將的某個嚴格子區間記作
題目把 interval 定義爲值域大小和區間大小相等的
我們定義maximal interval爲不被任意一個interval完整包含的interval
有一個重要結論:如果兩個maximal interval 相交,那麼區間一定符合interval的定義,又因爲是maximal interval,所以若他們相交,那麼並一定就是
考慮算出的全排列裏所有不合法的排列
依據這個結論,我們可以將不合法的排列分爲兩類
類型存在兩個maximal interval ,使
類型A爲大於等於3個maximal interval挨着連在一起組成
我們先考慮類型,它可以分爲和相交或相鄰兩種情況
若和相交,記
,
轉化完後得到的是相鄰的,因此我們只要考慮相鄰的情況
顯然可以發現的值域分別爲(證明可以用反證)
不妨假設在的左側(右側的情況方案數是相同的)
爲了避免重複計數,我們強制令的prefix中除了自己外,沒有任何一個prefix滿足值域爲且爲interval (若存在這樣的prefix,可以通過上面那個相交變相鄰的轉化轉爲不存在)
那麼就可以統計數量了,記爲的所有排列中滿足 任何一個prefix都不滿足值域爲且爲interval 的排列數量
有
接着考慮類型
對於某個maximal interval ,他內部是任意排的,我們唯一要滿足的限制是不存在某一段連續的maximal interval可以拼成一個interval
這個限制相當於這些maximal interval的排列是interval-free的
由於我們統計的是非法方案,因此至少存在一個maximal interval 的大小是>=2的,於是這些maximal interval的個數<n,就化成了子問題
爲了計算這種的方案數,需要一個輔助的dp
記爲將劃分成段,每段內部任意排列的方案數
有
然後就可以算答案了,記爲的所有排列中interval-free的排列數
maya我的語言表達太差了,寫的題解長度可能是原題解的2倍…
code:
#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn = 405;
int n,mod;
inline void add(int &a,const int &b){ a+=b;if(a>=mod)a-=mod; }
inline void dec(int &a,const int &b){ a-=b;if(a<0) a+=mod; }
int s[maxn];
int f[maxn],ans[maxn];
int g[maxn][maxn];
int main()
{
//freopen("tmp.in","r",stdin);
//freopen("tmp.out","w",stdout);
int Tcase; scanf("%d%d",&Tcase,&mod);
s[0]=1; for(int i=1;i<maxn;i++) s[i]=(ll)s[i-1]*i%mod;
f[1]=1;
for(int i=2;i<maxn;i++)
{
f[i]=s[i];
for(int j=1;j<i;j++) dec(f[i],(ll)f[j]*s[i-j]%mod);
}
g[0][0]=1;
for(int i=1;i<maxn;i++) for(int j=1;j<=i;j++)
{
for(int k=j-1;k<i;k++) add(g[i][j],(ll)g[k][j-1]*s[i-k]%mod);
}
ans[1]=1;
for(int i=2;i<maxn;i++)
{
ans[i]=s[i];
for(int j=1;j<i;j++) if(i-j>1||j>1)
dec(ans[i],2ll*f[j]%mod*s[i-j]%mod);
for(int k=3;k<i;k++) dec(ans[i],(ll)g[i][k]*ans[k]%mod);
}
while(Tcase--)
{
scanf("%d",&n);
printf("%d\n",ans[n]);
}
return 0;
}
先寫這麼多剩下的心情好的時候填坑