传送门:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1327
思路:这道傻逼的
noip
题竟然做了我一小会
23333
,还是弱啊
事实上很简单,我们注意到可以按列
dp
,类似于插入的方式,既然没法直接做,我们我们记录有多少列被空出来,于是设
f[i][j][k]
为到第
i
列,有
复杂度:
O(M2∗N)
代码:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<string>
#include<algorithm>
#define N 52
#define M 202
using namespace std;
struct node { int l,r;};
node a[N];
const int P = 1000000007;
int n,m,f[M][M][N],C[M][M],ans,fact[M];
//f[i][j][k]表示到第i列,剩下j个列没放,目前到第i列还剩下k个right未满足
//转移?
bool cmp(node x,node y){ return x.l < y.l;}
inline void up(int &a,int b){
a += b;
if (a >= P) a -= P;
}
void init(){
scanf("%d%d",&n,&m);
for (int i = 1;i <= n; ++i) {
scanf("%d%d",&a[i].l,&a[i].r);
a[i].r = m - a[i].r + 1; }
sort(a + 1,a + n + 1,cmp);
memset(C,0,sizeof(C));
for (int i = 0;i < M; ++i) C[i][0] = 1;
for (int i = 1;i < M; ++i)
for (int j = 1;j <= i; ++j)
up(C[i][j],C[i - 1][j] + C[i - 1][j - 1]);
fact[0] = 1;
for (int i = 1;i < M; ++i) fact[i] = 1LL * fact[i - 1] * i % P;
for (int i = 1;i < M; ++i)
for (int j = 1;j <= i; ++j)
C[i][j] = 1LL * C[i][j] * fact[j] % P;
}
void DO_IT(){
int ll,rr,block;
memset(f,0,sizeof(f));
f[0][0][0] = 1;
for (int i = 0;i < m; ++i){
ll = rr = block = 0;
for (int j = 1;j <= n; ++j) ll += (a[j].l == i + 1),rr += (a[j].r == i + 1),block += ((a[j].l < i + 1)&&(a[j].r > i + 1));
for (int j = 0;j <= i + 1; ++j)
for (int k = 0;k <= n - rr; ++k)
{
// printf("%d %d %d :%d\n",i,j,k,f[i][j][k]);
if (j + 1 >= ll&&ll >= 1) up(f[i + 1][j + 1 - ll][k + rr],1LL * ll * C[j][ll - 1] % P * f[i][j][k] % P);
if (j >= ll) {
up(f[i + 1][j + 1 - ll][k + rr],1LL * C[j][ll] * f[i][j][k] % P);
if (k + rr >= 1) up(f[i + 1][j - ll][k + rr - 1],1LL * f[i][j][k] * C[j][ll] % P * (k + rr) % P);
if (block) up(f[i + 1][j - ll][k + rr],1LL * f[i][j][k] * C[j][ll] % P * block % P);
}
}
}
ans = 0;
for (int i = 0;i <= m; ++i) up(ans,f[m][i][0]);
}
int main(){
init();
DO_IT();
cout<<ans;
return 0;
}
总结:1.写代码的时候一定要专心!
2.注意要分清楚排列数和组合数。。。不要总是搞错