传送门
看到数据范围就觉得这一定是一个
n
2
∗
m
n^2*m
n2∗m或者
n
∗
m
2
n*m^2
n∗m2的
d
p
q
w
q
dp\ qwq
dp qwq
果然是这样,因为每一列都是填一个或者不填,所以一定是按列
d
p
dp
dp
但这个 d p dp dp和以往有些不同,一般 d p dp dp状态设计都只跟当前状态有关,并不关心前面的和后面的,但因为这道题中 l i 和 r i l_i和r_i li和ri有一些不同,当当前枚举的列数 i i i不断增大时,有一些之前可以填的 l l l到后面就不能填了,但 r r r只要当前能填,后面就一直能填 . . .
所以就有了带延迟的动态规划:
也就是说我们可以把当前可以处理但却没有处理的状态记下来,到后面因为某些限制而必须处理的时候再处理
设 f [ i ] [ j ] [ k ] f[i][j][k] f[i][j][k]表示当前处理到 i i i列,前面空下了 j j j列,当前可以处理的 r r r有 k k k个没有处理的方案数,当我们还没有碰到某个 l l l的边界,就不会填,只有碰到 l l l的边界的时候,再从前面空下的 j j j列中找一列填上,这个可以用排列计算;而对于 r r r,只要碰到了边界,我们就可以选择处理或者不处理;当然这一列还可以选择不填或者填在 l , r l,r l,r中间的那一段,也就是既不在 l l l里,也不再 r r r里的 . . .
而且对这个状态从 i − 1 i-1 i−1向 i i i转移很不好写,还需要很多东西,我们可以从 i i i转移到它能够转移的状态去
代码如下:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#define N 55
#define M 205
#define LL long long
using namespace std;
int n,m,l[N],r[N],al[M],ar[M],cl,cr,tot;
LL f[M][M][N],fac[M],inv[M],ans;
const int mod=1e9+7;
inline int rd(){
int x=0,f=1;char c=' ';
while(c<'0' || c>'9') f=c=='-'?-1:1,c=getchar();
while(c<='9' && c>='0') x=x*10+c-'0',c=getchar();
return x*f;
}
inline LL qpow(LL x,int k){
LL ret=1;
while(k){
if(k&1) (ret*=x)%=mod;
(x*=x)%=mod; k>>=1;
} return ret;
}
inline void pre(){
fac[1]=1;
for(int i=2;i<=m+1;i++) fac[i]=fac[i-1]*i%mod;
inv[m+1]=qpow(fac[m+1],mod-2);
for(int i=m+1;i;i--) inv[i-1]=inv[i]*i%mod;
}
inline LL P(int n,int m){
if(m==0) return 1;
if(n<m) return 0;
// if(n==m) return 1;多加了这个特判wa了一次我怕不是个zz
return fac[n]*inv[n-m]%mod;
}
inline void add(LL &x,LL y){
x+=y; x>=mod?x-=mod:0;
}
int main(){
n=rd(); m=rd();
for(int i=1;i<=n;i++) l[i]=rd(),r[i]=rd(),al[l[i]]++,ar[m-r[i]+1]++;
pre(); f[1][0][0]=1;
for(int i=1;i<=m;i++){
tot+=al[i-1]-ar[i];
for(int j=max(0,al[i]-1);j<=i-1-cl;j++)
for(int k=0;k<=cr;k++){
if(!f[i][j][k]) continue;
if(al[i])//i这一列填在l上
add(f[i+1][j-al[i]+1][k+ar[i]],f[i][j][k]*P(j,al[i]-1)%mod*al[i]%mod);
if(k+ar[i] && j>=al[i])//i这一列填在r上,注意这里是k+ar[i]
add(f[i+1][j-al[i]][k+ar[i]-1],f[i][j][k]*P(j,al[i])%mod*(k+ar[i])%mod);
if(tot && j>=al[i])//i这一列填在既不是l也不是r的位置上
add(f[i+1][j-al[i]][k+ar[i]],f[i][j][k]*P(j,al[i])%mod*tot%mod);
if(j>=al[i])//i这一列空下
add(f[i+1][j-al[i]+1][k+ar[i]],f[i][j][k]*P(j,al[i])%mod);
}
cl+=al[i]; cr+=ar[i];
}
for(int i=0;i<=m;i++) (ans+=f[m+1][i][0])%=mod;
printf("%lld\n",ans);
return 0;
}
/*
3 7
1 2
4 3
2 1
*/