组合数学

格子计数

从坐标 \((0,0)\) 出发,每次可以向上向右走一格,求走到 \((n,m)\) 位置的方案数。

通常情况下可以用 dp,我们答案也可以是 \(\binom{n+m}{n}\),意思是总共 \(n+m\) 步选 \(n\) 步向上走其他向右走的方案数。

合法括号数

在长度为 \(2n\) 的括号序列中合法括号序列个数,合法括号为任意前缀左括号数量大于右括号数量,整个序列左括号和右括号数量相同。

可以想到可以用操作来形容括号向上就是右括号向右就是左括号,我们不合法的操作是碰触到直线 \(y=x+1\),我们把之后的操作进行折叠那最终不合法的点一定会到达点 \((n-1,n+1)\)

image

于是我们就得到结论:到达点 \((n-1,n+1)\) 的方案数恒穿过直线 \(y=x+1\),故是不合法括号序列数量。

总方案数减去不合法方案数就是合法括号序列方案数。

int fac[N]={1,1};
int c(int a,int b){
return fac[a]/fac[b]/fac[a-b];
}
signed main(){
int n=4;
for(int i=2;i<N;i++){
fac[i]=fac[i-1]*i%mod;
}
cout<<c(2*n,n)-c(2*n,n+1)<<" ";
a[0][0]=1;
for(int i=0;i<=n;i++){
for(int j=0;j<=n;j++){
if(j<i) continue;
a[i][j]+=a[i-1][j]+a[i][j-1];
}
}
cout<<a[n][n];
return 0;
}

P1641 [SCOI2010] 生成字符串

和上面同样做法,不过注意我们是按照红线路径对折,\(x,y\) 要互换后平移。

image

#include <bits/stdc++.h>
#define ll long long
#define int ll
#define ls p<<1
#define rs p<<1|1
#define re register
#define pb push_back
#define pir pair<int,int>
#define f(a,x,i) for(int i=a;i<=x;i++)
#define fr(a,x,i) for(int i=a;i>=x;i--)
#define lb(x) x&(-x);
using namespace std;
const int N=2e6+10;
const int M=8e6+10;
const int mod=20100403;
int n;
int fac[N];
int inv[N];
int ifac[N];
int c(int a,int b){
return fac[a]*ifac[b]%mod*ifac[a-b]%mod;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(nullptr);
int n,m;
cin>>n>>m;
fac[0]=fac[1]=1;
inv[0]=inv[1]=1;
ifac[0]=ifac[1]=1;
for(int i=2;i<N;i++){
fac[i]=fac[i-1]*i%mod;
inv[i]=inv[mod%i]*(mod-mod/i)%mod;
ifac[i]=ifac[i-1]*inv[i]%mod;
}
cout<<(c(n+m,n)-c(n+m,n+1)+mod)%mod;
return 0;
}
posted @   sad_lin  阅读(17)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示