[JOISC2020] 最古の遺跡 3
[JOISC2020] 最古の遺跡 3
题目背景
JOI 教授是一名研究 IOI 王国的历史学家。
题目描述
他发现了一行古代石柱的废墟及一份古代文献。
古代文献上的记载如下:
- 刚建造完成的时候,有 \(2\times N\) 个石柱,对于 \(1\le k\le N\) 均有两个石柱高度为 \(k\),同时记第 \(i\) 个石柱的高度为 \(h_i\)。
- 会发生 \(N\) 次地震,每次地震会使一些石柱的高度 \(-1\),其他石柱高度不变。
- 石柱 \(i\) 地震时高度不变,当且仅当 \(h_i\ge 1\) 并且对于 \(j>i\) 都要有 \(h_i\not=h_j\)
- \(N\) 次地震后,恰好只剩下了 \(N\) 个石柱。
现在 JOI 教授找出了仅存的 \(N\) 个石柱的位置 \(A_1,A_2,\ldots,A_N\),他想让你求出,最初 \(2\times N\) 个石柱高度的修建方案数 \(\bmod~10^9+7\) 的值。
输入格式
第一行为一个整数 \(N\)。
接下来一行 \(N\) 个数,表示 \(A_1,A_2,\ldots,A_N\)。
输出格式
仅一行一个整数,表示最初 \(2\times N\) 个石柱高度的建造方案数 \(\bmod~10^9+7\) 的值。
样例 #1
样例输入 #1
3
3 4 6
样例输出 #1
5
样例 #2
样例输入 #2
1
1
样例输出 #2
0
样例 #3
样例输入 #3
10
5 8 9 13 15 16 17 18 19 20
样例输出 #3
147003663
提示
样例解释
样例 1 解释
一种可行的解为 \((2,2,3,3,1,1)\)。
- 第一次地震后,变为 \((1,2,2,3,0,1)\)。
- 第二次地震后,变为 \((0,1,2,3,0,1)\)。
- 第三次地震后,变为 \((0,0,2,3,0,1)\)。
另外四种解如下:
- \((2,3,2,3,1,1)\)。
- \((2,3,3,2,1,1)\)。
- \((3,2,2,3,1,1)\).
- \((3,2,3,2,1,1)\)。
样例 2 解释
对于 \(N=1\) 的情况,显然只有 \((1,1)\) 一种修建方案,在一次地震后,会变为 \((0,1)\),\(1\) 号位置不可能有石柱。
样例 3 解释
共有 \(111147004440\) 种可能的修建方案,\(111147004440 \bmod 10^9+7=147003663\)。
子任务
对于 \(100\%\) 的数据,保证 \(1\le N\le 600\),\(1\le A_i\le 2\times N\),\(A_i< A_{i+1}\)。
Subtask 编号 | \(N\le\) | 分数 |
---|---|---|
\(1\) | \(13\) | \(6\) |
\(2\) | \(60\) | \(52\) |
\(3\) | 无 | \(42\) |
先强制钦定两个相同高度的是不一样的,最后再除回去。 ``
先想在给出 \(h\) 的情况下,如何不模拟找到 \(A\)。
看题解可知倒序枚举 \(h\)。用一个 \(vs\) 数组表示在最终态时有哪些数出现过(容易发现每种数最多出现一次)。如果 \(vs_{h_i}\),那么\(h_i\) 至少被减了一次,\(--h_i\),直到 \(h_i=0\) 或者他没有出现过。如果没有出现过,那么他不可能被删除,这个位置就属于 \(A\)。
那么现在给出了 \(A\),我们要倒退回原来的 \(h\) 的方案数。容易状压 \(n2^n\)。考虑给某一个 不在 \(A\) 的数填的时候有多少种方案。考虑维护 \(vs\) 数组的信息,他不在 \(A\) 中说明他在 \(vs\) 数组的前缀1中。那么就记录前缀1的数量。定义 \(f _{i,j}\) 表示考虑到倒数第 \(i\) 个数,\(vs\) 数组前缀有 \(j\) 个1的方案数。那么如果后面有 \(c\) 个数是不属于 \(A\) 的,就有 \(j-c\) 种方案。
现在看如果倒数第 \(i\) 个数是在 \(A\) 中的,那现在有两种情况。一种是前缀1的个数没有改变,那么这里的系数之后再考虑。如果前缀1的个数发生了改变,说明他填到了 \(j+1\) 处,枚举此时的前缀1的个数是 \(k\)。如果后面总共有 \(s\) 个属于 \(A\) 的点,那么方案乘上 \(\binom{s-j}{k-j-1}\times dp_{k-j-1}\times (k-j+1)\),\(dp_i\) 表示有 \(i\) 个连续的数的填的方案数,\(k-j+1\) 表示这个柱子的高度。
考虑求出 dp 值。\(dp_i=\sum\limits_{j=1}^idp_{j-1}\times dp_{i-j}\times\binom{i-1}{j-1}\times (j+1)\),分析同上。
#include<bits/stdc++.h>
using namespace std;
const int N=1205,P=1e9+7;
int dp[N],f[N],g[N],n,m,a[N],C[N][N],v[N];
int main()
{
scanf("%d",&n),m=n<<1;
for(int i=C[0][0]=1;i<N;i++)
{
C[i][0]=C[i][i]=1;
for(int j=1;j<N;j++)
C[i][j]=(C[i-1][j]+C[i-1][j-1])%P;
}
for(int i=1;i<=n;i++)
scanf("%d",a+i),v[m-a[i]+1]=1;
dp[0]=1;
for(int i=1;i<=m;i++)
for(int j=1;j<=i;j++)
(dp[i]+=1LL*dp[j-1]*dp[i-j]%P*C[i-1][j-1]%P*(j+1)%P)%=P;
f[0]=1;
int c=0,s=0;
for(int i=1;i<=m;i++)
{
if(!v[i])
{
for(int j=c;j<=n;j++)
f[j]=1LL*f[j]*(j-c)%P;
++c;
}
else
{
++s;
for(int j=s;j>=c;j--)
for(int k=j+1;k<=s;k++)
(f[k]+=1LL*C[s-j-1][k-j-1]*dp[k-j-1]%P*(k-j+1)%P*f[j]%P)%=P;
}
}
for(int i=1;i<=n;i++)
f[n]=f[n]&1? f[n]+P>>1:f[n]>>1;
printf("%d\n",f[n]);
}