1. 程式人生 > >【BZOJ 3907】網格 組合數學

【BZOJ 3907】網格 組合數學

cstring struct print == rime .html cst 個數 opera

大家說他是卡特蘭數,其實也不為過,一開始只是用卡特蘭數來推這道題,一直沒有懟出來,後來發現其實卡特蘭數只不過是一種組合數學,我們可以退一步直接用組合數學來解決,這道題運用組合數的思想主要用到補集與幾何法。

技術分享

假設以矩形左下角為坐標原點,(以下所說路徑均滿足只能向右或向上走),我們假設原矩陣為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; }

【BZOJ 3907】網格 組合數學