2014百度之星初赛第一轮解题报告:grids

Grids
时间限制:5s  内存限制:65536K

问题描述
度度熊最近很喜欢玩游戏。这一天他在纸上画了一个2行N列的长方形格子。他想把1到2N这些数依次放进去,但是为了使格子看起来优美,他想找到使每行每列都递增的方案。不过画了很久,他发现方案数实在是太多了。度度熊想知道,有多少种放数字的方法能满足上面的条件?

输入
第一行为数据组数                              。
然后T行,每行为一个数 表示长方形的大小。

输出
         对于每组数据,输出符合题意的方案数。由于数字可能非常大,你只需要把最后的结果对1000000007取模即可。

样例输入
2
1
3
样例输出
Case #1:
1
Case #2:
5

提示
对于第二组样例,方案为:
  
  

共5种方案。

解题报告 – Grids
我们考虑将1,2,…,2n依次填入2xn的格子中,易见,1只能填入昨下角的格子中,2可以填入1右侧或者1的上面,且不能填入其他位置。下面我们按照此法填充:
Step 1.
  
  


1


Step 2.
  
  


1
2

or
  
2
  


1


Step 3.
  
  


1
2
3
or
  
3
  


1
2

or
  
2
  


1
3

不难观察得出,在这两行格子中,填充的格子总是连续的,否则就会违反规则,并且,上行格子填充的进度必须小于等于下行格子填充的进度。这很类似于台阶问题:在一个n*n的格子中,我们从左下角出发,前往右上角,每一步,只能水平向右走一格,或者垂直向上走一格,并且不能越过对角线。如果我们将水平走一格对应为将当前的数填入下行,将垂直向上走一格对应为将当前的数填入上行,则可见这两个问题完全等价。那么填充格子的可行方案数等于台阶问题中从左下角到达右上角的路径数,等于c(2n,n)-c(2n,n-1)=c(2n,n)/(n+1),也就是著名的Catalan数。我们可以预先计算好12n的阶乘模1000000007,并且利用欧几里得算法计算它们对于1000000007的逆,那么cn=(2n)!/((n+1)!n!)=(2n)!*rev((n+1)!)*rev(n!),其中rev表示逆。


解题代码:

#include <iostream>

const int N = 1000000; const int MOD = 1000000007; long long p[N + N + 1]; long long rev[N + N + 1]; void ext_gcd(long long x, long long y, long long *a, long long *b) { if (y == 0) { *a = 1; *b = 0; } else { ext_gcd(y, x % y, a, b); long long t = *a; *a = *b; *b = t - *b * (x / y); } } void precompute() { p[0] = 1; for (int i = 1; i <= N + N; ++i) { p[/color][i][color=#000000] = p[i - 1] * i % MOD; long long a; long long b; ext_gcd(p[/color][i][color=#000000], MOD, &a, &b); rev[/color][i][color=#000000] = (a % MOD + MOD) % MOD; } } int main(int argc, char *argv[]) { precompute(); int T; std::cin >> T; for (int c = 1; c <= T; ++c) { int n; std::cin >> n; fprintf(stdout, "Case #%d: ", c); long long res = p[n + n] * rev[n + 1] % MOD * rev[n] % MOD; fprintf(stdout, "%d ", static_cast<int>(res)); } return 0; } /* vim: set expandtab ts=4 sw=4 sts=4 tw=100: */


原文地址:https://www.cnblogs.com/hosealeo/p/4190513.html