zoukankan      html  css  js  c++  java
  • 辽宁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 }
  • 相关阅读:
    C# 图片与Base64的相互转化
    LeetCode 303. Range Sum Query – Immutable
    LeetCode 300. Longest Increasing Subsequence
    LeetCode 292. Nim Game
    LeetCode 283. Move Zeroes
    LeetCode 279. Perfect Squares
    LeetCode 268. Missing Number
    LeetCode 264. Ugly Number II
    LeetCode 258. Add Digits
    LeetCode 257. Binary Tree Paths
  • 原文地址:https://www.cnblogs.com/ivorysi/p/5796076.html
Copyright © 2011-2022 走看看