51nod 1327 棋盘游戏

传送门:http://www.51nod.com/onlineJudge/questionCode.html#!problemId=1327
思路:这道傻逼的 noip 题竟然做了我一小会 23333 ,还是弱啊
事实上很简单,我们注意到可以按列 dp ,类似于插入的方式,既然没法直接做,我们我们记录有多少列被空出来,于是设 f[i][j][k] 为到第 i 列,有j列空出来,到第 i 列有k right 未满足条件,然后暴力转移就可以了。
复杂度: OM2N

代码:

#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.注意要分清楚排列数和组合数。。。不要总是搞错

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值