【GDOI2014模拟】网格

6 篇文章 0 订阅
2 篇文章 0 订阅

Description

求从(0,0)点走到(n,m)点不越过直线y=x的方案数。
n,m<=5000

Solution

首先,这是一个经典问题,相信大家都会做。
正难则反,我们用总数减去不合法的方案数。
总数很明显是 Cmm+n
然后,因为题目要求是不越过,我们把直线向上平移一格,变成y=x+1,
然后作出原点关于这条直线的对称点(-1,1),那么不和法的方案数就是 Cm1m+n
不过这道题没有要求取模%%%
于是就要上高精度了。。。
不过,我们可以把上下分解质因数(其实也不用),然后再一一抵消掉,就不用写高精除了。

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define N 10000
using namespace std;
const int maxn=100000;
struct num{
    int shu[N];
    friend num operator-(num y,num z) {
        num x;memset(x.shu,0,sizeof(x.shu));x.shu[0]=y.shu[0];
        fo(i,1,x.shu[0]) {
            x.shu[i]+=y.shu[i]-z.shu[i];
            if (x.shu[i]<0) x.shu[i]+=maxn,x.shu[i+1]--;
        }
        while (!x.shu[x.shu[0]]) x.shu[0]--;
        return x;
    }
    friend num operator*(num y,int z) {
        num x;memset(x.shu,0,sizeof(x.shu));x.shu[0]=y.shu[0];
        fo(i,1,x.shu[0]) {
            x.shu[i]+=y.shu[i]*z;
            x.shu[i+1]+=x.shu[i]/maxn;
            x.shu[i]%=maxn;
        }
        while (x.shu[x.shu[0]+1]) x.shu[0]++;
        return x;
    }
}ans;
int p[N+5],c[N+5],n,m;
void recout(int x,int f) {
    for(;x!=1;x/=p[x]) c[p[x]]+=f;
}
num C(int m,int n) {
    num x;memset(x.shu,0,sizeof(x.shu));
    x.shu[0]=x.shu[1]=1;
    memset(c,0,sizeof(c));
    fo(i,n+1,m) recout(i,1);
    fo(i,1,m-n) recout(i,-1);
    fo(i,1,N) fo(j,1,c[i]) x=x*i;
    return x;
}
int main() {
    fo(i,2,N) if (!p[i]) fo(j,1,N/i) p[i*j]=i;
    scanf("%d%d",&n,&m);
    ans=C(m+n,m)-C(m+n,m-1);
    printf("%d",ans.shu[ans.shu[0]]);
    fd(i,ans.shu[0]-1,1) printf("%05d",ans.shu[i]);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值