zoukankan      html  css  js  c++  java
  • 【BZOJ 3907】网格 组合数学

    大家说他是卡特兰数,其实也不为过,一开始只是用卡特兰数来推这道题,一直没有怼出来,后来发现其实卡特兰数只不过是一种组合数学,我们可以退一步直接用组合数学来解决,这道题运用组合数的思想主要用到补集与几何法。

    假设以矩形左下角为坐标原点,(以下所说路径均满足只能向右或向上走),我们假设原矩阵为a,那么他关于l(y=x+1),对称矩形就是b(黑色),那么出现了c矩阵,他的长为n+1,宽为m-1,易知从(0,0)到(n,m)(a右上角)的路径(在矩形a内)的种数就是C(n+m,m),然后我告诉你从(0,0)到(m-1,n+1)(c右上角)的路径(在矩形c内)种数C(n+m,m-1),就是原矩阵中不合法路径个数,你不信很正常.....

    那么让我们想一下。从(0,0)到(n,m)的不合法路径(在矩形a内)一定满足若干次碰到了l与a围成的三角型的边界之一——l在a内部分,然后在最后一次碰到后离开并驶向(n,m),从(0,0)到(m-1,n+1)的路径(在矩形c内)均满足若干次碰到了l与a围成的三角型,然后在最后一次碰到后离开并驶向(m-1,n+1),再然后我们发现在“离开”之前的走法满足以上两种路径可以吻合,那么“离开“之后呢——他离开时一定最后与l交于一点,那么我们发现在l上的任意整点(在a内部分)与(n,m)和(m-1,n+1)分别作为两个对角点形成的矩形全等,于是从一角到另一角的方案一一对应,于是证毕。

    #include <cstdio>
    #include <cstring>
    const int P=10000;
    struct Bigint{
      int a[5000];
      Bigint(){a[0]=a[1]=1;}
      inline friend Bigint operator - (Bigint a,Bigint b);
      inline friend Bigint operator * (Bigint a,int b);
      inline void operator -= (Bigint b){(*this)=(*this)-b;}
      inline void operator *= (int b){(*this)=(*this)*b;}
      inline void print();
    }ans1,ans2;
    inline void Bigint:: print(){
      printf("%d",a[a[0]]);
      for(int i=a[0]-1;i>0;--i)
        printf("%04d",a[i]);
    }
    inline Bigint operator - (Bigint a,Bigint b){
      for(int i=1;i<=a.a[0];++i){
        a.a[i]-=b.a[i];
        if(a.a[i]<0)
          a.a[i]+=P,a.a[i+1]--;
      }
      while(a.a[a.a[0]]==0)a.a[0]--;
      return a;
    }
    inline Bigint operator * (Bigint a,int b){
      int last=0;
      for(int i=1;i<=a.a[0];++i)
        a.a[i]=a.a[i]*b+last,last=a.a[i]/P,a.a[i]%=P;
      if(last)a.a[++a.a[0]]=last;
      return a;
    }
    int n,m;
    int prime[P+10],len,mini[P+10];
    bool isnot[P+10];
    inline void get_prime(){
      for(int i=2;i<=P;++i){
        if(isnot[i]==false)prime[++len]=i,mini[i]=len;
        for(int j=1;prime[j]*i<=P;++j){
          isnot[prime[j]*i]=true,mini[prime[j]*i]=j;
          if(i%prime[j]==0)break;
        }
      }
    }
    int size[P];
    int main(){
      scanf("%d%d",&n,&m),get_prime();
      for(int i=n+m,x;i>n;--i){
        x=i;
        while(mini[x])
          ++size[mini[x]],x/=prime[mini[x]];
      }
      for(int i=1,x;i<=m;++i){
        x=i;
        while(mini[x])
          --size[mini[x]],x/=prime[mini[x]];
      }
      for(int i=1;i<=len;++i)
        while(size[i])
          ans1*=prime[i],--size[i];
      for(int i=n+m,x;i>n+1;--i){
        x=i;
        while(mini[x])
          ++size[mini[x]],x/=prime[mini[x]];
      }
      for(int i=1,x;i<m;++i){
        x=i;
        while(mini[x])
          --size[mini[x]],x/=prime[mini[x]];
      }
      for(int i=1;i<=len;++i)
        while(size[i])
          ans2*=prime[i],--size[i];
      ans1-=ans2;
      ans1.print();
      return 0;
    }
  • 相关阅读:
    做统计图的好工具
    QueryBuildRange中的表达式
    四种方式话Equal
    QueryBuildRange的空值
    GetHashCode()初探
    X++中的字符串操作函数
    寻找缺陷的方法
    字程序级别的重构
    代码大全的方向
    多线程啊
  • 原文地址:https://www.cnblogs.com/TSHugh/p/7617643.html
Copyright © 2011-2022 走看看