您的位置:首页 > 其它

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

2015-04-08 18:03 218 查看

链接:

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


题解:

~~~~~ 首先从 (0,0)(0,0) 到 (n,m)(n,m) 是右走 nn 步, 上走 mm 步。方案数是在 nn 个数中 n+1n+1 个空中插 mm 个数,组合数是Cmn+mC_{n+m}^m。

~~~~~ 然后从中减去穿过 y=xy = x 这条线的那些方案数就好了。

~~~~~ 但是这个很难求,而若要求 [ 不经过 ] 则就可以有特别的技巧了。

~~~~~ 假设第一步向上,那么最终无论如何都要经过这条线,因为向上已经走一步,所以需要去掉的方案数则为 Cm−1n+m−1C_{n+m-1}^{m-1} 。

~~~~~ 然后如果第一步向右,则若一个方案从起点到最后一次经过 y=xy=x 这条线的那个点的整个路径都沿 y=xy=x 这条直线翻转,剩余到终点 (n,m)(n,m) 的路径不进行变动,则正好对应唯一一种第一步向上走的方案。而这种情况的方案数则为 Cn−1n+m−1C_{n+m-1}^{n-1} 。

~~~~~ 所以求不经过,答案则是 【全集】Cmn+m−【补集】2∗Cm−1n+m−1【全集】C_{n+m}^m - 【补集】2*C_{n+m-1}^{m-1}

~~~~~ 那么我们怎么转化成不穿过呢?

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

所以最终答案是 【全集】Cmn+m+1−【补集】2∗Cm−1n+m【全集】C_{n+m+1}^m - 【补集】2*C_{n+m}^{m-1}

特殊的骗分技巧:

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)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: