题解 CF367E

传送门

首先 \(n>m\) 时显然无解。由原题的数据范围可得 \(n\le \sqrt{10^5}\)

考虑如何构造出满足条件的 \(n\) 个区间。不难发现对于任意两个不同区间 \([l_i,r_i],[l_j,r_j]\) ,都有 \(l_i<l_j,r_i<r_j\) 即可。那么我们如果选取了 \(n\) 个左端点和 \(n\) 个右端点,有且仅有一种满足题意的构造方式,也就是将左右端点分别排序后按序号匹配。

下面考虑如何计算总方案数。定义 \(dp[i][j][k]\) 为考虑完前 \(i\) 个位置,目前有 \(j\) 个左端点被选择,\(k\) 个右端点被选择。而右端点总是要与左端点匹配,所以需要保证 \(j\ge k\) 。注意到关键点时只能考虑向 \(k+1\) 转移。

时间复杂度:\(O(mn^2)\)

 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
#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define ll long long
using namespace std;
inline ll read(){
    ll x=0,f=1;char ch=getchar();
    while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
    while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
    return x*f;
}
const int N=1e5+5,P=1e9+7;
int n,m,x,fac[N];
inline void Add(int &x,const int &y){x+=y;if(x>P)x-=P;}
int main(){
    n=read(),m=read(),x=read();
    if(n>m)return puts("0"),0;
    int dp[2][n+5][n+5];
    memset(dp,0,sizeof(dp));
    dp[0][0][0]=1;
    rep(i,1,m){
        int now=i&1,pre=now^1;
        memset(dp[now],0,sizeof(dp[now]));
        rep(j,0,n)rep(k,0,j){//第i个位置,j个左括号,k个右括号
            if(j>k&&i!=x)Add(dp[now][j][k+1],dp[pre][j][k]);
            Add(dp[now][j+1][k],dp[pre][j][k]);
            Add(dp[now][j+1][k+1],dp[pre][j][k]);
            if(i!=x)Add(dp[now][j][k],dp[pre][j][k]);
        }
    }
    fac[0]=1;
    rep(i,1,n)fac[i]=1ll*fac[i-1]*i%P;
    printf("%lld\n",1ll*dp[m&1][n][n]*fac[n]%P);
    return 0;
}

Comments