luogu P7213 [JOISC2020] 最古の遺跡 3
首先发现原来题面的形式不好直接做,于是考虑换一种角度思考:
维护\(B\)数组,每个位置\(i\)表示这种高度的石柱最后留下的位置在哪里。从\(2n\)到\(1\)逆序扫过来,并将每个数\(j\)从\(j\)到\(1\)看,如果找到第一个\(B\)中没有数的位置填进去。如果没有没有填过的位置就不在\(B\)中出现。
考虑把这个东西塞进dp里,发现对于一个不在最后序列中的数,它所能选择的数的范围取决于当前\(B0从\)\(1\)开始连续段的长度。因此我们设\(dp_{i,j}\)表示从后往前到了\(i\),从\(1\)开始连续段长度为\(j\)的方案数。
考虑转移,不在最终序列中的转移是平凡的,碰到一个在最终序列中的位置考虑其是否对当前答案造成改变,不改变不发生转移,改变了枚举改变到的位置,中间长度为\(k\)的一段需要满足两个条件:其中恰好有\(k\)个数,并对于任意前缀,数的个数不大于当前位置。容易发现这个就是卡特兰数。因此转移可以\(O(1)\),总复杂度\(O(n^3)\)
code:
#include<bits/stdc++.h>
#define I inline
#define ll long long
#define db double
#define lb long db
#define N (600+5)
#define M ((N<<1)+5)
#define K (1500+5)
#define mod 1000000007
#define Mod (mod-1)
#define eps (1e-5)
#define ull unsigned ll
#define it iterator
#define Gc() getchar()
#define Me(x,y) memset(x,y,sizeof(x))
#define Mc(x,y) memcpy(x,y,sizeof(x))
#define d(x,y) ((k+1)*(x)+(y))
#define R(n) (1ll*rand()*rand()%(n)+1)
#define Pc(x) putchar(x)
#define LB lower_bound
#define UB upper_bound
#define PB push_back
using namespace std;
int n,x,C1,C2,Fl[M];ll dp[M][N],frc[N],Inv[N],g[N],W[N];
I ll mpow(ll x,int y=mod-2){ll Ans=1;while(y) y&1&&(Ans=Ans*x%mod),y>>=1,x=x*x%mod;return Ans;}
int main(){
freopen("1.in","r",stdin);
int i,j,h;scanf("%d",&n);for(i=1;i<=n;i++) scanf("%d",&x),Fl[x]=1;for(frc[0]=Inv[0]=i=1;i<=n;i++) frc[i]=frc[i-1]*i%mod,Inv[i]=mpow(frc[i]);
g[0]=W[0]=1;for(i=1;i<=n;i++){for(j=i;j;j--) g[j]=(g[j-1]+g[j])%mod;for(j=i;j;j--) g[j]=(g[j-1]+g[j])%mod;W[i]=g[i];}
dp[2*n+1][0]=1;for(i=2*n;i;i--){
if(Fl[i]){C1++;
Mc(dp[i],dp[i+1]);for(j=0;j<=C1;j++){
for(h=j+1;h<=C1;h++) dp[i][h]=(dp[i][h]+dp[i+1][j]*frc[C1-j-1]%mod*Inv[C1-h]%mod*W[h-j-1]%mod*(h-j+1))%mod;
}
}else{C2++;for(j=C2;j<=C1;j++) dp[i][j]=dp[i+1][j]*(j-C2+1)%mod;}
}printf("%lld\n",dp[1][n]*mpow(2,mod-n-1)%mod);
}