CodeForces 367 E.Sereja and Intervals(组合数学+dp)

本文探讨了在特定条件下计算合法括号序列数量的方法。给定长度为n的区间,放置n对括号,要求任一括号不被其他括号包含,并且至少有一个左括号位于指定位置x。通过动态规划算法求解满足条件的括号序列总数。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

在长度为n的区间上放n对扩号,使得任一括号不被其他括号包含,且至少有一个左括号在x位置,问满足条件的括号序列个数

Input

三个整数n,m,x(1nm105,1xm)

Output

输出满足条件的括号序列个数,结果模109+7

Sample Input

1 1 1

Sample Output

1

Solution

先不考虑括号间的相对顺序,求出有顺序的括号序列个数再乘上n!即为答案,首先一个位置不可能出现两个相同的括号,这样必然会有一个区间包含另一个区间,故一个位置只有四种情况,左括号,右括号,左括号和右括号,不放括号;每个位置括号放好后,括号序列固定,因为最后一个右括号必然匹配最后一个左括号,否则最后一个左括号会匹配一个位置在最后一个右括号之前的右括号,那么就会出现包含,以此类推确定每对括号,故问题变成一个简单dp,令dp[i][j][k]表示前i个位置放j个左括号和k个右括号的方案数,然后每个位置按当前位置的四种情况递推即可,注意只有当前位置不是x时才能放右括号或者不放括号,第x个位置要么放一个左括号要么放左右括号,n!dp[m][n][n]即为答案

Code

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
typedef long long ll;
typedef pair<int,int>P;
const int INF=0x3f3f3f3f,maxn=333;
#define mod 1000000007
int n,m,x,dp[2][maxn][maxn],fact[maxn];
void init(int n=330)
{
    fact[0]=1;
    for(int i=1;i<=n;i++)fact[i]=(ll)i*fact[i-1]%mod;
}
void add(int &x,int y)
{
    x=x+y>=mod?x+y-mod:x+y;
}
int main()
{
    //只要一个端点不出现两个及以上的左括号或右括号,那么n个左括号和n个右括号对应唯一的互不包含的区间 
    scanf("%d%d%d",&n,&m,&x);
    init();
    if(n>m)printf("0\n");
    else
    {
        dp[0][0][0]=1;
        int cur=0;
        for(int i=1;i<=m;i++)
        {
            cur^=1;
            for(int j=0;j<=n;j++)
                for(int k=0;k<=j;k++)
                {
                    dp[cur][j][k]=0;
                    if(j&&k)add(dp[cur][j][k],dp[cur^1][j-1][k-1]);//[]
                    if(j-1>=k)add(dp[cur][j][k],dp[cur^1][j-1][k]);//[
                    if(i!=x)
                    {
                        if(k)add(dp[cur][j][k],dp[cur^1][j][k-1]);//]
                        add(dp[cur][j][k],dp[cur^1][j][k]);//NULL
                    }
                }
        }
        int ans=(ll)dp[cur][n][n]*fact[n]%mod;
        printf("%d\n",ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值