BZOJ3907网格

这东西是拿Cat思想搞得组合数学。

首先做这个需要会用网格法或折线法分析Cat的$C_{2n}^n-C_{2n}^{n-1}$是怎么来的。

网格法:假如没有限制,从(0,0)到(n,n)的方案数为$C_{2n}^n$,就是一共有2n次操作位置(向右或向上),我们把向上走的操作插入这些位置即得上式,上面的黄线是当我们走到不合法情况时所碰到的第一条线,然后最终我们会走到(n,n)这个点,如果我们将矩形沿着这条线翻折,我们碰到黄线,然后走到(n,n)的走法,可以映射为碰到黄线,然后走到(n-1,n+1)的走法,因为对称嘛。

而我们碰到黄线之前的走法在矩阵翻折中是不受影响的,这样,不合法方案数就是从(0,0)走到(n-1,n+1)的方案数,这个分析和上面差不多,总共$C_{2n}^{n-1}$种,全部的减去不合法的,就是合法的,$C_{2n}^n-C_{2n}^{n-1}$

折线法:就是从(0,0)到(2n,0),每次只能沿y=x或y=-x走一个单位,最后图像整体没有位于x下方的部分的方案数。没有限制的话,总方案数为$C_{2n}^n$,因为可以把折线拆成2n段,那么有n段上扬,n段下跌。不能越过x轴,从第一次碰到y=-1这条线开始就不合法了,我们把这个点以后的折线沿y=-1翻折,由于最后到达(2n,0)点,翻折后就到达(2n,-2),此时问题转化为从(0,0)到(2n,-2)的方案数,有n段,n-1段上扬,n+1段下跌,方案数$C_{2n}^{n-1}$全部的减去不合法的,就是合法的,$C_{2n}^n-C_{2n}^{n-1}$

那这个题就简单多了,同样用上面的方法,把一个n换成m就差不多了

答案就是$C_{n+m}^n-C_{n+m}^{m-1}$,下面的代码是化简后的式子,没有高精减,高精除用唯一分解刚过去,而且时间复杂度也还好,就打的n√n的拆分,n的拆分(我自己证的,可能是假的)在下一篇博客里,(因为那个题√n拆过不去QAQ)。

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<vector>
#include<queue>
#include<stack>
#include<set>
#include<map>
using namespace std;
int n,m;
int prime[5000],prime_num;
bool v[10050];
int fz[5000],fm[5000];
struct Bigint{
    int a[90000],len;
    void clear(){
        memset(a,0,sizeof(a));
        a[1]=1;
        len=1;
    }
    friend void operator * (Bigint &x,int y){
        int delta=0;
        for(int i=1;i<=x.len;i++){
            x.a[i]=x.a[i]*y+delta;
            delta=x.a[i]/10;
            x.a[i]%=10;
        }
        while(delta>0){
            x.a[++x.len]=delta%10;
            delta/=10;
        }
        while(x.a[x.len]==0&&x.len>1)
            x.len--;
    }
    void out(){
        for(int i=len;i>=1;i--)
            printf("%d",a[i]);
    }
}ans;
void doprime(){
    for(int i=2;i<=10005;i++){
        if(!v[i]) prime[++prime_num]=i;
        for(int j=1;j<=prime_num&&i*prime[j]<=10005;j++){
            v[i*prime[j]]=1;
            if(i%prime[j]==0) break;
        }
    }
}
void mulfz(int x){
    for(int i=1;i<=prime_num;i++)
        while(x%prime[i]==0){
            fz[i]++;
            x/=prime[i];
        }
}
void mulfm(int x){
    for(int i=1;i<=prime_num;i++)
        while(x%prime[i]==0){
            fm[i]++;
            x/=prime[i];
        }
}
int main(){
    scanf("%d%d",&n,&m);
    doprime();ans.clear();
    for(int i=2;i<=n+m;i++)
        mulfz(i);
    mulfz(n-m+1);
    for(int i=2;i<=m;i++)
        mulfm(i);
    for(int i=2;i<=n+1;i++)
        mulfm(i);
    /*for(int i=1;i<=prime_num;i++)
        cout<<prime[i]<<" ";cout<<endl;*/
    for(int i=1;i<=prime_num;i++){
        for(int j=1;j<=fz[i]-fm[i];j++)
            ans*prime[i];
    }
    ans.out();
    puts("");
    return 0;
}
View Code

 

转载于:https://www.cnblogs.com/Yu-shi/p/11222174.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值