• 辽宁OI2016夏令营模拟T3-chess


    放棋子(chess.pas/c/cpp)
    题目大意
    现在有一个 n*m 的棋盘,现在你需要在棋盘上摆放 2n 个棋子,要求满足如下条件:
    1、 每一列只能有一个棋子;
    2、 每一行的前 xi 个格子有一个棋子,而且最多有一个棋子;
    3、 每一行的后 yi 个格子有一个棋子,而且最多有一个棋子;
    求一共有多少种不同的放置方案,答案对于 1000000007 取模
    输入文件
    输入文件为 chess.in。
    输入共有 n+1 行。第一行有两个整数 n,m,表示该棋盘的行数与列数。
    接下来的 n 行,每行两个整数 xi 和 yi,表示每一行的前 xi 个格子需要有一个棋子,每
    一行的后 yi 个格子需要有一个棋子。
    输出文件
    输出文件为 chess.out。
    输出一个整数表示共有多少种不同的方案,答案对于 1000000007 取模。
    样例输入
    3 6
    1 2
    3 3
    3 2
    样例输出
    4
    数据规模与约定
    n<=50 m<=200
    对于所有的 i,有 xi+yi≤m。

    ——————————————————————————题解

    首先60分我们只需要拿乘法原理算算算就可以了(把x,y排列顺序不影响结果,那就变成排列后左边对于第一行有x1中方法,第二行有x2-1种,第n行有xn-n+1种,乘法原理乘起来就好了,右边同理,然后左右方案数相乘)因为最大的xi和最大的yi不会重合,但是如果他们最大重合了就要换一个做法

    首先我们左边从小到大,右边从大到小这么想,因为xi+yi≤m,所以方块排序后不会重叠,只是一列可能有两种颜色

    然后从左往右扫

    然后我们对右边来说事,也是从左往右扫,才能把两者结合在一起

    然后我们发现这只与i(扫到第几行),k(选了几个数),j(右边可以放几个)有关,然后我们把两边结合到一起

    得到:

    不在右边放f[i+1][j+p][k+z]+=f[i][j][k]*A(i-k,z)

    在右边放f[i+1][j-1+p][k+z+1]+=f[i][j][k]*j

    最后的答案是f[m+1][0][2*n],因为到了m列的时候右边还是可以放的

    啊累死我了……本题结束了……下面是代码……

     1 #include <queue>
     2 #include <cstdio>
     3 #include <vector>
     4 #include <cstring>
     5 #include <algorithm>
     6 #define mo  1000000007 
     7 #define siji(i,x,y) for(int i=(x);i<=(y);i++)
     8 #define gongzi(j,x,y) for(int j=(x);j>=(y);j--)
     9 #define xiaosiji(i,x,y) for(int i=(x);i<(y);i++)
    10 #define sigongzi(j,x,y) for(int j=(x);j>(y);j--)
    11 #define pii pair<int,int>
    12 #define fi first
    13 #define se second
    14 using namespace std;
    15 int n,m,x[55],y[55],z[205],p[205],b[205],c[205];
    16 int a[205][105];
    17 void anm() {
    18     siji(i,0,200) a[i][0]=1;
    19     siji(i,1,200) {
    20         siji(j,1,100) {
    21             a[i][j]=1LL*i*a[i-1][j-1]%mo;
    22         }
    23     }
    24 }
    25 void init() {
    26     scanf("%d%d",&n,&m);
    27     if(m<2*n) {puts("0");exit(0);} 
    28     siji(i,1,n) {
    29         scanf("%d%d",&x[i],&y[i]);
    30         z[x[i]]++;
    31         p[m-y[i]]++;//这是下一列要有的右端开头
    32     }
    33     siji(i,1,m) b[i]=b[i-1]+p[i-1];//这是个常数优化,加上后快到飞起
    34     siji(i,1,m) c[i]=c[i-1]+z[i];//同上
    35     anm();//组合数预处理
    36 }
    37 int f[205][55][205];//f(i,j,k)
    38 void solve() {
    39     f[1][0][0]=1;//初始化
    40     siji(i,1,m) {
    41         siji(j,0,b[i]) {//可以改成0-n
    42             siji(k,c[i-1],i) {//可以改成0-m
    43                 f[i+1][j+p[i]][k+z[i]]=(f[i+1][j+p[i]][k+z[i]]+1LL*f[i][j][k]*a[i-k][z[i]])%mo;
    44                 if(j-1+p[i]>=0)
    45                     f[i+1][j-1+p[i]][k+z[i]+1]=(f[i+1][j-1+p[i]][k+z[i]+1]+1LL*j*f[i][j][k]*a[i-k-1][z[i]])%mo;
    46 
    47             }
    48         }
    49     }
    50     printf("%d
    ",f[m+1][0][2*n]);
    51 }
    52 int main()
    53 {
    54     freopen("chess.in","r",stdin);
    55     freopen("chess.out","w",stdout);
    56     init();
    57     solve();
    58 }
  • 相关阅读:
    P1908 逆序对
    P3834 【模板】可持久化线段树 1(主席树)
    BZOJ 4300: 绝世好题
    Codevs 2185【模板】最长公共上升子序列
    P1439 【模板】最长公共子序列
    P3865 【模板】ST表
    【转】良心的可持久化线段树教程
    Codevs 1299 切水果
    P3388 【模板】割点(割顶)&& 桥
    P3805 【模板】manacher算法
  • 原文地址:https://www.cnblogs.com/ivorysi/p/5796076.html
Copyright © 2020-2023  润新知