【BZOJ】【3907】网格

组合数学/python


3907: 网格

Time Limit: 1 Sec  Memory Limit: 256 MB
Submit: 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

[Submit][Status][Discuss]

  题面很容易想到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;
View Code

   UPD:(2015年4月19日 20:21:03)

  Orz ykz神犇,提供了他的题解&高精C++代码:

我们假设0表示向右走,1表示向上走,那么很显然问题可以转化为:给你n个0和m个1,求出满足某个条件的01串的个数,这个条件是——对于任意一个子串s[1…i],0的个数不小于1的个数。我们可以用补集转化的方法,所有的01串的个数为$\binom{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串的个数就是$\binom{n+m}{m-1}$。然后就可以出解啦,为了避免除以0(因为有m-1)我们这么搞:$$ans=\binom{n+m}{m}-\binom{n+m}{n+1} ( \binom{n+m}{m-1}=\binom{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("\n");
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 
View Code

 

posted @ 2015-03-20 18:26  Tunix  阅读(1046)  评论(0编辑  收藏  举报