I I I
感觉这题非常厉害
为了方便解释,做以下约定
1. 1. 1.将全序列 p 1 , p 2 . . . p n p_1,p_2...p_n p1,p2...pn记为 A A A
2. 2. 2.将 A A A的某个严格子区间记作 B i B_i Bi
题目把 interval 定义为值域大小和区间大小相等的 B i B_i Bi
我们定义maximal interval为不被任意一个interval完整包含的interval
有一个重要结论:如果两个maximal interval B x , B y B_x,B_y Bx,By相交,那么区间 B x ∪ B y B_x∪B_y Bx∪By一定符合interval的定义,又因为 B x , B y B_x,B_y Bx,By是maximal interval,所以若他们相交,那么并一定就是 A A A
考虑算出 n n n的全排列里所有不合法的排列
依据这个结论,我们可以将不合法的排列分为两类
类型 1 : 1: 1:存在两个maximal interval B u , B v B_u,B_v Bu,Bv,使 B u ∪ B v = A B_u∪B_v=A Bu∪Bv=A
类型 2 : 2: 2:A为大于等于3个maximal interval挨着连在一起组成
我们先考虑类型 1 1 1,它可以分为 B u B_u Bu和 B v B_v Bv相交或相邻两种情况
若 B u B_u Bu和 B v B_v Bv相交,记
B w = B u ∩ B v , B u ′ = B u − B w , B v ′ = B v + B w B_w=B_u∩B_v,B_{u'}=B_u-B_w,B_{v'}=B_v+B_w Bw=Bu∩Bv,Bu′=Bu−Bw,Bv′=Bv+Bw,
转化完后得到的 B u ′ 和 B v ′ B_{u'}和B_{v'} Bu′和Bv′是相邻的,因此我们只要考虑 B u , B v B_u,B_v Bu,Bv相邻的情况
显然可以发现 B u , B v B_u,B_v Bu,Bv的值域分别为 [ 1 , k ] , [ k + 1 , n ] [1,k],[k+1,n] [1,k],[k+1,n](证明可以用反证)
不妨假设 B u B_u Bu在 B v B_v Bv的左侧(右侧的情况方案数是相同的)
为了避免重复计数,我们强制令 B u B_u Bu的prefix中除了自己外,没有任何一个prefix满足值域为 [ 1 , k ] [1,k] [1,k]且为interval (若存在这样的prefix,可以通过上面那个相交变相邻的转化转为不存在)
那么就可以统计数量了,记 f n f_n fn为 n n n的所有排列 A A A中满足 任何一个prefix都不满足值域为 [ 1 , k ] [1,k] [1,k]且为interval 的排列数量
有 f n = n ! − ∑ i = 1 n − 1 ( f i ∗ ( n − i ) ! ) f_n=n!-\sum_{i=1}^{n-1}(f_i*(n-i)!) fn=n!−i=1∑n−1(fi∗(n−i)!)
接着考虑类型 2 2 2
对于某个maximal interval B u B_u Bu,他内部是任意排的,我们唯一要满足的限制是不存在某一段连续的maximal interval可以拼成一个interval
这个限制相当于这些maximal interval的排列是interval-free的
由于我们统计的是非法方案,因此至少存在一个maximal interval B v B_v Bv的大小是>=2的,于是这些maximal interval的个数
记 g n , k g_{n,k} gn,k为将 n n n划分成 k k k段,每段内部任意排列的方案数
有 g n , k = ∑ i = k − 1 n − 1 g i , k − 1 ∗ ( n − i ) ! g_{n,k}=\sum_{i=k-1}^{n-1}g_{i,k-1}*(n-i)! gn,k=∑i=k−1n−1gi,k−1∗(n−i)!
然后就可以算答案了,记 a n s n ans_n ansn为 n n n的所有排列 A A A中interval-free的排列数
a n s n = n ! − 2 ∑ i = 1 n − 1 f i ∗ ( n − i ) ! − ∑ k = 3 n − 1 g n , k ∗ a n s k ans_n=n!-2\sum_{i=1}^{n-1}f_i*(n-i)!-\sum_{k=3}^{n-1}g_{n,k}*ans_k ansn=n!−2i=1∑n−1fi∗(n−i)!−k=3∑n−1gn,k∗ansk
maya我的语言表达太差了,写的题解长度可能是原题解的2倍…
code:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#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;
}
先写这么多剩下的心情好的时候填坑