题意
n×m的棋盘,放棋子,要求第i行左边lil_ili个格子和右边rir_iri个格子各恰好有一个棋子,且每一列最多只能有1个棋子,问方案数,对109+710^9+7109+7取模
n≤50,m≤200n\leq50,m\leq200n≤50,m≤200
Solution
这是一道DP好题。
太菜了以至于我想了半天连状态都没想到。
考虑到每一列最多放一个,那么考虑在列上做文章,设f[i][]f[i][]f[i][]表示做到了第i列……
然后怎么办呢,对于一列,假如一些行左区间到此为止了,那么就一定要将这些行放完,将这些行安排之前没有被放过的列;否则先把它们咕咕了。如果一些行的右区间从这一列开始,那么没有处理完的行就会多出几个。其次你也可以选择填在中间没有覆盖的地方。
如果这么考虑的话,状态就很好想了(可是就是没有这么考虑)
设f[i][j][k]f[i][j][k]f[i][j][k]表示做到了第i列,在之前有j列没有被放过棋子,有k行已经到了右区间。
记li,ri,midil_i,r_i,mid_ili,ri,midi分别为左区间右端点为i、右区间左端点为i、i列没有区间覆盖住的行数
每一列的转移要添加ri+1r_{i+1}ri+1的右区间放置机会,要将l[i+1]l[i+1]l[i+1]全部搞定。
初状态为f[0][0][0]=1f[0][0][0]=1f[0][0][0]=1
考虑三种转移:
- 不放在中间空着的,也不放在右区间,那么就要将li+1l_{i+1}li+1行安排前面没放棋子的那j列f[i+1][j+1−li+1][k+ri+1]+=f[i][j][k]∗Aj+1li+1f[i+1][j+1-l_{i+1}][k+r_{i+1}]+=f[i][j][k]*A_{j+1}^{l_i+1}f[i+1][j+1−li+1][k+ri+1]+=f[i][j][k]∗Aj+1li+1
- 放在右区间f[i+1][j−li+1][k+ri+1−1]+=f[i][j][k]∗Ajli+1∗(k+ri+1)f[i+1][j-l_{i+1}][k+r_{i+1}-1]+=f[i][j][k]*A_j^{l_{i+1}}*(k+r_{i+1})f[i+1][j−li+1][k+ri+1−1]+=f[i][j][k]∗Ajli+1∗(k+ri+1)
- 放在中间空着的位置f[i+1][j−li+1][k+ri+1]+=f[i][j][k]∗Ajli+1∗midi+1f[i+1][j-l_{i+1}][k+r_{i+1}]+=f[i][j][k]*A_j^{l_{i+1}}*mid_{i+1}f[i+1][j−li+1][k+ri+1]+=f[i][j][k]∗Ajli+1∗midi+1
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(int i(a),_E_(b);i<=_E_;++i)
#define REP(i,a,b) for(int i(a),_E_(b);i<_E_;++i)
#define fd(i,a,b) for(int i(a),_E_(b);i>=_E_;--i)
#define ll long long
#define mo 1000000007
#define N 52
#define M 210
using namespace std;
int f[M][M][N],l[M],r[M],mid[M],n,m,p[M][M];
int main(){
scanf("%d %d",&n,&m);
fo(i,0,m){
p[i][0]=1;fo(j,1,i)p[i][j]=1ll*p[i][j-1]*(i-j+1)%mo;
}
fo(i,1,n){
int L,R;scanf("%d %d",&L,&R);
++l[L];++r[m-R+1];++mid[L+1];--mid[m-R+1];
}
fo(i,2,m)mid[i]+=mid[i-1];
f[0][0][0]=1;
REP(i,0,m)fo(j,0,i)fo(k,0,n)if(f[i][j][k]){
if(j+1>=l[i+1])(f[i+1][j+1-l[i+1]][k+r[i+1]]+=1ll*p[j+1][l[i+1]]*f[i][j][k]%mo)%=mo;
if(j>=l[i+1]&&k+r[i+1])(f[i+1][j-l[i+1]][k-1+r[i+1]]+=1ll*p[j][l[i+1]]*(k+r[i+1])%mo*f[i][j][k]%mo)%=mo;
if(j>=l[i+1])(f[i+1][j-l[i+1]][k+r[i+1]]+=1ll*p[j][l[i+1]]*mid[i+1]%mo*f[i][j][k]%mo)%=mo;
}int ans=0;fo(i,0,m)ans=(ans+f[m][i][0])%mo;
printf("%d",ans);
return 0;
}