太绝了。
首先容易想到要用容斥,具体的,我们钦定区间集合 \(S=\{[l,r]|p_{l...r}\text{是} l...r \text{的排列}\}\),贡献为 \((-1)^{|S|}\) 乘上对应方案数。
然后仔细观察,不难发现对于选出来的区间 \([l_1,r_1],[l_2,r_2]\),若满足 \(l_1<l_2<r_1<r_2\),则 \([l_1,l_2-1],[l_2,r_1],[r_1+1,r_2]\) 也满足对应条件。
考虑 trick,我们能否利用容斥系数抵消掉一些贡献。对于上述的两个区间,有 \(m_{l_1}\ge r_1\)。而 \(l_2<r_1\le m_{l_1}\),我们显然可以多选择出一个合法的区间 \([l_1,l_2-1]\)。这样方案数不变,容斥系数会取反。
进一步的,我们可以构造双射:找出 \(S\) 中最靠前的两对相交区间 \([l_1,r_1],[l_2,r_2]\),然后若存在 \([l_1,l_2-1]\) 则删除,否则加入 \([l_1,l_2-1]\),这样就把所有 \(S\) 两两匹配起来,系数抵消。
所以,我们只需要考虑不存在严格相交的区间即可,此时每对区间的关系要么相交,要么相离。建成树状,我们考虑对于每个区间,枚举它包含的最外层的区间进行 dp。
设 \(f[l,r]\) 表示仅填写 \(p_{l...r}\),且 \(S\) 中的区间都是 \([l,r]\) 的子区间,此时的答案(方案数乘容斥系数之和)。特别的,\([l,r]\notin S\)。
枚举 \(l\),设 \(g[r,x]\) 表示 \(l...r\) 中有 \(x\) 个位置没有被 \(S\) 中的区间包含,在此前提下的答案,利用 \(g\) 辅助转移。有:
\[g[r,x]=g[r-1,x-1]-\sum_{i=l+1}^r g[i-1,x]f[i,r] \]\[f[l,r]=\sum_{x=0}^{r-l+1} x!\cdot g[r,x] \]求出每个 \(f[l,r]\) 后,更新 \(g[r,0]\gets g[r,0]-f[l,r]\)。
然后这题就做完了,时间复杂度 \(O(n^4)\)。
当然还能这样 dp:
我们发现如果两个区间 \([l_1,r_1],[l_2,r_2]\) 满足 \(l_1<l_2<r_1=r_2\),其实这种方案仍然可以双射抵消,但是并非严格相交。
考虑设 \(f[l,r,0/1]\) 表示钦定的 \(S\) 不包含/包含右端点 \(=r\) 的,其中 \(f[l,r,1]\) 中 \(S\) 可以选 \([l,r]\),此时的答案。
那么 \(f[l,r,1]=\sum\limits_{x=0}^{r-l+1} x!g[r,x],\space f[l,r+1,0]=\sum\limits_{x=0}^{r-l+1} (x+1)! g[r,x]\)。
\(g[r,x]=g[r-1,x-1]+\sum\limits_{i=l}^r g[i-1,x]f[i,r,0]\)
最终答案是 \(f[1,n,1]\)。
总结思路:
-
容斥,但是要先确定容斥什么,这个很重要
-
观察性质,有什么可以把复杂的东西简化
-
容斥系数可以构造双射来抵消,一个小 trick
点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define pir pair<ll,ll>
#define fi first
#define se second
#define mkp make_pair
#define pb push_back
using namespace std;
const ll maxn=210, mod=1e9+7;
ll n,a[maxn],f[maxn][maxn],g[maxn][maxn],fac[maxn];
int main(){
scanf("%lld",&n);
for(ll i=1;i<=n;i++) scanf("%lld",a+i);
if(a[1]==n){
puts("0"); return 0;
}
fac[0]=1;
for(ll i=1;i<=n;i++) fac[i]=fac[i-1]*i%mod;
for(ll i=n;i;i--){
memset(g,0,sizeof g);
g[i-1][0]=1;
for(ll j=i;j<=n;j++){
for(ll c=0;c<=j-i+1;c++){
if(c) g[j][c]=g[j-1][c-1];
for(ll k=i+1;k<=j;k++)
if(a[k]>=j) g[j][c]=(g[j][c]-g[k-1][c]*f[k][j])%mod;
f[i][j]=(f[i][j]+g[j][c]*fac[c])%mod;
}
if(j<=a[i]) g[j][0]=(g[j][0]-f[i][j])%mod;
}
}
printf("%lld",(f[1][n]+mod)%mod);
return 0;
}