码迷,mamicode.com
首页 > 其他好文 > 详细

【BZOJ3907】网格 组合数,补集转换

时间:2015-04-09 09:08:03      阅读:196      评论:0      收藏:0      [点我收藏+]

标签:bzoj3907   网格   组合数   

链接:

#include <stdio.h>
int main()
{
    puts("转载请注明出处[vmurder]谢谢");
    puts("网址:blog.csdn.net/vmurder/article/details/44944381");
}

题解:

      首先从 (0,0)(n,m) 是右走 n 步, 上走 m 步。方案数是在 n 个数中 n+1 个空中插 m 个数,组合数是Cmn+m
      然后从中减去穿过 y=x 这条线的那些方案数就好了。

      但是这个很难求,而若要求 [ 不经过 ] 则就可以有特别的技巧了。
      假设第一步向上,那么最终无论如何都要经过这条线,因为向上已经走一步,所以需要去掉的方案数则为 Cm?1n+m?1
      然后如果第一步向右,则若一个方案从起点到最后一次经过 y=x 这条线的那个点的整个路径都沿 y=x 这条直线翻转,剩余到终点 (n,m) 的路径不进行变动,则正好对应唯一一种第一步向上走的方案。而这种情况的方案数则为 Cn?1n+m?1
      所以求不经过,答案则是 Cmn+m?2?Cm?1n+m?1

      那么我们怎么转化成不穿过呢?
      观察发现第一步向上,则定然不符合要求需要被删掉;而第一步向右,满足要求的所有方案,因为无法经过 y=x 这条直线,所以它们被一条无形的线 y=x?1 禁锢着,只要越过这条线就会碰触到 y=x 。所以 [ 不经过 ] 情况下的询问 (n,m) 正好对应着 [ 不穿过 ] 情况下的询问 (n?1,m)

所以最终答案是 Cmn+m+1?2?Cm?1n+m

特殊的骗分技巧:

n==m时答案是卡特兰数哦骚年~
这个可以打表发现!

代码:

def Fac(n):
    ans=1
    for i in range(2,n+1):
        ans=ans*i
    return ans
def C(n,m):
    if (n<m):
        return 0
    return (Fac(n)/Fac(m)/Fac(n-m))
n,m=raw_input().split()
n=int(n)
m=int(m)
print C(n+m+1,m)-2*C(n+m,m-1)

【BZOJ3907】网格 组合数,补集转换

标签:bzoj3907   网格   组合数   

原文地址:http://blog.csdn.net/vmurder/article/details/44944381

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!