组合数学/python
3907: 网格
Time Limit: 1 Sec Memory Limit: 256 MBSubmit: 162 Solved: 76
[Submit][Status][Discuss]
Description
某
城市的街道呈网格状,左下角坐标为A(0, 0),右上角坐标为B(n, m),其中n >= m。现在从A(0,
0)点出发,只能沿着街道向正右方或者正上方行走,且不能经过图示中直线左上方的点,即任何途径的点(x, y)都要满足x >=
y,请问在这些前提下,到达B(n, m)有多少种走法。
Input
输入文件中仅有一行,包含两个整数n和m,表示城市街区的规模。
Output
输出文件中仅有一个整数和一个换行/回车符,表示不同的方案总数。
Sample Input
6 6
Sample Output
132
HINT
100%的数据中,1 <= m <= n <= 5 000
Source
题面很容易想到Catalan数……但是5000的范围实在是有些吃不消……
题解:http://www.cnblogs.com/mhy12345/p/4343980.html
copy了下代码sorry……
UPD:(2015-04-02 16:53:03)
好吧我还是来写一下吧:
我们求不越过$y=x$这条线的方案数不是很好求,那么我们就利用补集转化的思想来求。首先所有方案的总数是$C(n+m,n)$,其中所有不合法的方案,即中途跨过了$ y=x $这条线的路径,我们都可以将跨越点之后的路径翻折一下,得到一条从(1,1)到(m,n)的路线,也就是说,所有不合法的方案数之和即为C(n+m,n-1)。容我三思QAQ,或者哪位路过的神犇指点我一下……
啊哩怎么跟我当初抄的代码不太一样= =?
1 /************************************************************** 2 Problem: 3907 3 User: Tunix 4 Language: Python 5 Result: Accepted 6 Time:1184 ms 7 Memory:79228 kb 8 ****************************************************************/ 9 10 def C(n,m): 11 return fact[n]/fact[m]/fact[n-m]; 12 f=raw_input().split(" "); 13 n=int(f[0]); 14 m=int(f[1]); 15 tot=max(n,m)*2; 16 fact=[1]; 17 for i in range(1,tot+1): 18 fact.append(fact[-1]*i); 19 c=n-m; 20 ans=C(tot-c,tot/2)-C(tot-c,tot/2+1); 21 print ans;
UPD:(2015年4月19日 20:21:03)
Orz ykz神犇,提供了他的题解&高精C++代码:
我们假设0表示向右走,1表示向上走,那么很显然问题可以转化为:给你n个0和m个1,求出满足某个条件的01串的个数,这个条件是——对于任意一个子串s[1…i],0的个数不小于1的个数。我们可以用补集转化的方法,所有的01串的个数为$inom{n+m}{m}$,然后我们再考虑不合法的01串个数。我们假定现在有一个01串,它的0和1的个数分别是n和m,第一次出现不满足条件的位置是i。即s[1…i]当中,0的个数=1的个数-1,并且s[i]=1。我们把s[1…i]的所有0变成1,1变成0,这样,我们得到的新的01串,这个串当中,0和1的个数分别为n+1,m-1。我们发现,这个转化是一一对应的,也就是说,每一个不合法的01串,都对应了一个唯一的一个n+1,m-1的01串;而这个n+1,m-1的01串也正好和唯一的这个不合法串对应,满足充要性。于是我们得到了不合法的01串的个数就是$inom{n+m}{m-1}$。然后就可以出解啦,为了避免除以0(因为有m-1)我们这么搞:$$ans=inom{n+m}{m}-inom{n+m}{n+1} ( inom{n+m}{m-1}=inom{n+m}{n+1})$$
1 /************************************************************** 2 Problem: 3907 3 User: Tunix 4 Language: C++ 5 Result: Accepted 6 Time:84 ms 7 Memory:944 kb 8 ****************************************************************/ 9 10 #include<cstdio> 11 #include<cstring> 12 13 typedef long long LL; 14 15 const int N=10001; 16 const LL mod=100000000; 17 18 int tot=0,x[N],p[N],v[N]={0}; 19 LL a[1000],b[1000]; 20 21 LL pow(LL x,int p) { 22 LL t=1;for (;p;p>>=1,x*=x) if (p&1) t*=x;return t; 23 } 24 25 void mul(LL a[],LL y) { 26 LL x=0,&l=a[0]; 27 for (int i=1;i<=l;i++) { 28 a[i]=a[i]*y+x; 29 x=a[i]/mod; 30 a[i]%=mod; 31 } 32 while (x) a[++l]=x%mod,x/=mod; 33 } 34 35 void dec(LL a[],LL b[]) { 36 LL &l=a[0]; 37 for (int i=1;i<=l;i++) { 38 if (a[i]<b[i]) a[i+1]--,a[i]+=mod; 39 a[i]-=b[i]; 40 } 41 while (!a[l]) l--; 42 } 43 44 void getc(LL a[],int n,int m) { 45 memset(x,0,sizeof x); 46 for (int i=2;i<=n;i++) x[i]++; 47 for (int i=2;i<=m;i++) x[i]--; 48 for (int i=2;i<=n-m;i++) x[i]--; 49 for (int i=n;i>=2;i--) 50 if (!v[i]) mul(a,pow(i,x[i])); 51 else x[v[i]]+=x[i],x[i/v[i]]+=x[i]; 52 } 53 54 void print(LL a[]) { 55 int l=a[0]; 56 printf("%lld",a[l]); 57 for (int i=l-1;i>=1;i--) printf("%08lld",a[i]); 58 printf(" "); 59 } 60 61 int main() { 62 int n,m; 63 scanf("%d%d",&n,&m); 64 for (int i=2;i<=n+m;i++) { 65 if (!v[i]) p[++tot]=i; 66 for (int j=1,k;j<=tot,(k=p[j]*i)<=n+m;j++) { 67 v[k]=p[j]; 68 if (i%p[j]==0) break; 69 } 70 } 71 a[0]=a[1]=b[0]=b[1]=1; 72 getc(a,n+m,n); 73 getc(b,n+m,n+1); 74 dec(a,b); 75 print(a); 76 return 0; 77 } 78