卡特兰数
应用场景:
1.n个节点能组成几种二叉树
2.括号匹配给出n对括号,求可以组成的合法表达式的个数
3.凸多边形的三角划分
4.一个顺序为1~n的进栈序列,有多少种出栈序列
卡特兰数求解代码(从0开始,用的组合数方法求):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| #include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=2e5+10; const int MOD=1e9+7; ll fact[N]; ll inv_fact[N]; ll qpow(ll x,ll y){ ll base=x,ret=1; while(y){ if(y&1) ret=ret*base%MOD; base=base*base%MOD; y>>=1; } return ret; } void init(){ fact[0]=inv_fact[0]=1; for(int i=1,inv;i<N;++i){ fact[i]=(fact[i-1]*i)%MOD; inv=qpow(i,MOD-2); inv_fact[i]=(inv_fact[i-1]*inv)%MOD; } } ll C(int a,int b){ return ((fact[a]*inv_fact[b])%MOD*inv_fact[a-b])%MOD; } void solve(){ init(); int n; cin>>n; ll ans=((C(2*n,n)-C(2*n,n-1))%MOD+MOD)%MOD; cout<<ans<<endl; } int main() { std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0); solve(); return 0; }
|
题的变形:
括号匹配给出n对括号,k种括号,求可以组成的合法表达式的个数
答案就成了n的卡特兰数*pow(k,n)
字符串不同子串个数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| #include<cstdio> #include<iostream> #define maxn 100005 #define re register int n,cnt=1,lst=1; long long ans; char S[maxn]; int fa[maxn<<1],len[maxn<<1],son[maxn<<1][26]; inline void ins(int c) { int f=lst,p=++cnt; lst=p; len[p]=len[f]+1; while(f&&!son[f][c]) son[f][c]=p,f=fa[f]; if(!f) {fa[p]=1;return;} int x=son[f][c]; if(len[f]+1==len[x]) {fa[p]=x;return;} int y=++cnt; len[y]=len[f]+1,fa[y]=fa[x],fa[x]=fa[p]=y; for(re int i=0;i<26;i++) son[y][i]=son[x][i]; while(f&&son[f][c]==x) son[f][c]=y,f=fa[f]; } int main() { scanf("%d",&n),scanf("%s",S+1); for(re int i=1;i<=n;i++) ins(S[i]-'a'); for(re int i=2;i<=cnt;i++) ans+=(long long)(len[i]-len[fa[i]]); printf("%lld\n",ans); return 0; }
|
字符串某一个子串的数目
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| #include<bits/stdc++.h> using namespace std; int dp[1010][1010]; string s,t; int main() { int len1,len2; cin>>s>>t; len1=s.size(); len2=t.size(); for(int i = 0 ; i <= len1 ; i++){ dp[i][0] = 1; } for(int i = 1 ; i <= len1 ; i++){ for(int j = 1 ; j <= len2 ; j++){ if(s[i - 1] == t[j - 1]) dp[i][j] = dp[i-1][j-1] + dp[i-1][j]; else dp[i][j] = dp[i - 1][j]; } } cout<<dp[len1][len2]; return 0; }
|
数组不同子序列个数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
| #include <bits/stdc++.h> using namespace std; #define ll long long const int N = 1e5+5; const int mod = 1000000007; int a[N]; ll dp[N]; int vis[N]; int main() { int n; while(cin >> n){ for(int i=1;i<=n;i++){ cin >> a[i]; } memset(dp,0,sizeof(dp)); memset(vis,0,sizeof(vis)); dp[1]=1; vis[a[1]]=1; for(int i=2;i<=n;i++){ if(vis[a[i]]==0){ dp[i] = (dp[i-1]*2 + 1)%mod; }else{ dp[i] = (dp[i-1]*2 - dp[ vis[a[i]] - 1 ] + mod) % mod; } vis[a[i]]=i; } cout<<dp[n]<<endl; } return 0; }
|
两端相等子数组最大和,返回最后一次相等左,右位置和
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| #include<bits/stdc++.h> using namespace std; int num[210000]; int main() { int t; cin>>t; while(t--){ int n,sum=0,sum1=0,l; cin>>n; for(int i=0;i<n;i++) cin>>num[i],sum+=num[i]; for(int i=0;i<n;i++){ sum1+=num[i]; if(sum1>=sum/2){ l=i; break; } } int r=l+1,sum2=sum-sum1; while(sum1!=sum2){ if(sum1>sum2) sum1-=num[l--]; else sum2-=num[r++]; } cout<<l+n-r+1<<endl; } return 0; }
|