您的位置:首页 > 其它

jzoj5316 【清华集训2017模拟8.19】merge

2017-08-20 20:21 519 查看

题意

两个1..n的排列,每次选一个将队首入栈,求有多少种不同的入栈序列。

分析

设fi,j没跑了,考虑如何去重。

转移f[i][j]的时候,假设i,j前面有一段长度为k的相同串。 我们可以发现,将A序列看作左括号,B序列看作右括号,指针从i-k,j-k开始的每一种合法括号序列都可以被对称操作从而获得一种重复。 (这能覆盖所有重复的情况)

因此我们枚举上一次指针的位置i-k,j-k,然后减去重复数。 (也就是合法括号序列的对数)

但是注意到类似 (()) () 这样的括号序列,已经被k’< k去过重了,因此要预先给左右加上一对大括号将整个括起来。所以括号对数减去一对。

合法括号序列的个数就是卡特兰数

demo

#include <cstdio>
#include <iostream>
using namespace std;
typedef long long ll;
const ll N=2e3+10,mo=1e9+7;
int n,ans;
int a
,b
;
int sj[N*2]
;
int f

,g

,c
;
inline void add(int &x,int y) {x=(ll)(x+y)%mo;}
ll ksm(ll x,ll y) {
if (y==1) return x;
ll z=ksm(x,y>>1);
return z*z%mo*((y&1)?x:1)%mo;
}
void init() {
sj[0][0]=1;
for (int i=1; i<=2*n; i++) for (int j=0; j<=n; j++) {
sj[i][j] = sj[i-1][j];
if (j) add(sj[i][j], sj[i-1][j-1]);
}
for (int i=0; i<=n; i++)
c[i] = (ll) ksm(i+1,mo-2) * sj[2*i][i] % mo;
for (int i=1; i<=n; i++) for (int j=1; j<=n; j++) if (a[i]==b[j]) {
g[i][j]=g[i-1][j-1]+1;
} else g[i][j]=0;
}
int main() {
freopen("merge.in","r",stdin);
freopen("merge.out","w",stdout);
cin>>n;
for (int i=1; i<=n; i++) scanf("%d",&a[i]);
for (int i=1; i<=n; i++) scanf("%d",&b[i]);
init();
f[0][0]=1;
for (int i=0; i<=n; i++) for (int j=0; j<=n; j++) {
if (i+j==0) continue;
f[i][j] = (f[i-1][j] + f[i][j-1]) % mo;
if (g[i][j]) {
int len=g[i][j];
for (int k=1; k<=len; k++)
add(f[i][j], (ll) - f[i-k][j-k] * c[k-1] % mo);
}
}
printf("%d\n",(f

+mo)%mo);
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: