问题一: 括号匹配问题
n
n
n个左括号,
n
n
n个右括号,要求左括号和右括号匹配,总方案数。
即中间不能出现前缀的右括号数量大于左括号。
考虑随机放的方案:
C
(
n
,
2
n
)
C(n,2 n)
C(n,2n)
考虑不合法的情况,如果我们把第一个不合法的位置设为
k
k
k,将
[
1
,
k
]
[1,k]
[1,k]翻转。
得到的左括号为
n
+
1
n+1
n+1。
那么对于一个左括号为
n
+
1
n+1
n+1,右括号为
n
−
1
n-1
n−1的随机放的方案能不能转换成原来的不合法方案呢。
如果左括号是
1
1
1,右括号是
−
1
-1
−1,对于
−
1
1
1
1
-1\ 1\ 1\ 1
−1 1 1 1,将前缀
1
1
1多的第一个位置开始往前翻转就可以了。
不合法方案数为
C
(
n
+
1
,
2
n
)
C(n+1,2n)
C(n+1,2n)
合法方案数为: C ( n , 2 n ) − C ( n + 1 , 2 n ) C(n,2n)-C(n+1,2n) C(n,2n)−C(n+1,2n)
类似的问题:
1、
D
c
k
y
w
o
r
d
s
Dcky\ words
Dcky words:
n
n
n个
X
X
X,
n
n
n个
Y
Y
Y,前缀满足
X
≥
Y
X\geq Y
X≥Y。
2、
2
n
2n
2n个人买票,只有
50
50
50和
100
100
100,售票处一开始没钱,一张票
50
50
50,求每个人有票不会发生无法找钱的情况的方案数。
3、标准
Y
o
u
n
g
Young
Young表,
2
∗
n
2*n
2∗n的矩阵,填
1
−
2
n
1-2n
1−2n的数,要求每一列和每一排都递增。
这道题我们令第一行为左括号,第二行为右括号,首先如果分好了第几行放法是唯一的,所以只要保证分的过程中,放在第二行的有足够多的数使得放在第一行的比他们大。
(此图是大佬的图)
4、不相交的弦:一个圆上
2
n
2n
2n个点,连线,得到
n
n
n条不相交的线。
从某个点开始,弦的第一个点为
(
(
(,第二个点为
)
)
)。即可。
(此图也是大佬的图)
做法二就是:转换成卡特兰数通用形式——
∑
f
(
i
)
f
(
n
−
i
)
\sum f(i)f(n-i)
∑f(i)f(n−i),枚举某条边的分割。
问题二:出栈序列
入栈序列为
1
2
3
.
.
.
n
1\ 2\ 3\ ...\ n
1 2 3 ... n,求方案数。
其实可以等价于括号匹配,因为要求先入才有出,入的数量大于等于出的数量
类似的问题:
1、
n
n
n个斜向上和
n
n
n个斜向下,画群峰的方案数[不能掉到水平线下]
做法一:括号匹配
做法二:考虑第一个回到水平点的位置,分割成子问题。
2、不穿越对角线的格路问题(从
(
0
,
0
)
(0,0)
(0,0)到
(
n
,
m
)
(n,m)
(n,m))
做法一:括号匹配
做法二:可以把其转个
135
135
135度变成群峰问题。
3、满二叉树的种数
前序遍历:通过类似
d
f
s
dfs
dfs,转换成出栈序列
中序遍历:分成子问题。
4、不出现"312"的全排列
一个长度为n的排列a,只要满足
i
<
j
<
k
i<j<k
i<j<k且
a
j
<
a
k
<
a
i
aj<ak<ai
aj<ak<ai就称这个排列为
312
312
312排列。
312
312
312是长度为
3
3
3不可能出现的出栈序列,也就是该问题可以变成出栈序列。
同理
213
213
213、
123
123
123都是一样的,虽然没有实际意义,但是和
312
312
312在答案上是等价的。
** 5、矩阵的阶梯划分**
给定一个阶梯,划分成多个矩阵。
见此处。
卡特兰数
f ( n ) = C 2 n n n + 1 = C 2 n n − C 2 n n + 1 = ∑ i = 0 n − 1 f ( i ) ∗ ( n − i − 1 ) f(n)=\frac{C^n_{2n}}{n+1}=C^n_{2n}-C^{n+1}_{2n}=\sum\limits_{i=0}^{n-1}f(i)*(n-i-1) f(n)=n+1C2nn=C2nn−C2nn+1=i=0∑n−1f(i)∗(n−i−1)
例题
洛谷1641 生成字符串
括号匹配
n
n
n和
m
m
m,保证
n
≥
m
n\geq m
n≥m
傻逼才不会的题,所以我是傻逼。等价于之前的问题,之前是把第一个不满足条件翻转,此题同理,反转后,
1
1
1的数量会多
1
1
1。
所以答案是
C
(
n
+
m
,
n
)
−
C
(
n
+
m
,
n
+
1
)
C(n+m,n)-C(n+m,n+1)
C(n+m,n)−C(n+m,n+1)。
洛谷P2532 树屋阶梯
任意大的
n
n
n个矩形搭建阶梯,每次差一格。
考虑到每个拐角一定有一个且一个矩形包含,每个矩形只有一个。
并且对于左下角必然被某个矩形包含,枚举被哪个包含了,就可以分成子问题了。
如果是最上面的拐角就是:
f
(
0
)
∗
(
f
n
−
1
)
f(0)*(fn-1)
f(0)∗(fn−1),第二个就是:
f
(
1
)
∗
f
(
n
−
2
)
f(1)*f(n-2)
f(1)∗f(n−2)…以此类推。
即卡特兰数。
洛谷题解里的图(盗用)
洛谷P3200 有趣的数列
2
n
2n
2n个数放到数列中,保证奇数项递增,偶数项递增,相邻:奇数
<
<
<偶数
可以发现,前面要小一点,后面才放的下。
对于每个偶数项,大于前面所有的数,
S
i
≥
2
∗
i
S_i\geq 2*i
Si≥2∗i,可以发现从
1
−
2
n
1-2n
1−2n,到
2
∗
i
2*i
2∗i,至多
i
i
i个偶数项。
也就是说,保证偶数项小于等于奇数项即可。
此题还要求非质数取模,晒个板子
#include<bits/stdc++.h>
#define FOR(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
typedef long long ll;
const int maxn = 2e6+510;
const int mod = 1e9+7;
struct C_not_prime_mod{
bool prime[maxn];
int p[maxn];
int cnt;
void isprime(){
cnt = 0;
memset(prime,true,sizeof(prime));
for(int i=2; i<maxn; i++){
if(!prime[i])continue;
p[cnt++] = i;
for(int j=i+i; j<maxn; j+=i)prime[j] = false;
}
}
ll quick_mod(ll a,ll b,ll m){
ll ans = 1;
a %= m;
while(b){
if(b & 1){
ans = ans * a % m;
b--;
}
b >>= 1;
a = a * a % m;
}
return ans;
}
ll Work(ll n,ll p){
ll ans = 0;
while(n){
ans += n / p;
n /= p;
}
return ans;
}
ll Solve(ll n,ll m,ll P){
ll ans = 1;
for(int i=0; i<cnt && p[i]<=n; i++){
ll x = Work(n, p[i]);
ll y = Work(n - m, p[i]);
ll z = Work(m, p[i]);
x -= (y + z);
ans *= quick_mod(p[i],x,P);
ans %= P;
}
return ans;
}
}cnpm;
int main(){
cnpm.isprime();
ll n,p;cin>>n>>p;
ll ans=(cnpm.Solve(2*n,n,p)-cnpm.Solve(2*n,n-1,p))%p+p;
cout<<ans%p<<endl;
}
某场 g y m gym gym J J J题
题意
现有一颗树,每个结点上有个标号,并且满足如下性质,子节点的标号值不小于父亲结点。
现在给出这棵树中序遍历的标号序列。
问有多少棵树满足以上条件。
题解
首先找到最小值,如果只有一个就其作为根,否则其先做出一棵树。
剩余部分插到这棵树上。
显然他们是连续的,否则根最小的性质不满足。
最小值分成了多个区间,插进去即可。
而最小值构成的这个树,显然是卡特兰数。
写法就是枚举每个点的贡献,即从大到小,一旦出现比自己小的数,就重新划分区间。
例如
3
3
3,这里的
3
3
3一定是被分了
3
3
3个,而
2
2
2也是
3
3
3个。
对于每个数每个区间都是一颗连续的树,所以直接卡特兰数相乘即可。
递归的方法复杂度是不够的。要求 O n On On,判断有没有更小的时候用 r m q rmq rmq。