这是白书二代上的一个组合计数里面的一道练习题。
我用的方法是枚举在x*y的方格中放置一条对角线,也可以理解为枚举斜率。
如图,先来看这样一个问题,n=m=4,即此时有3行3列的方格的时候,可以放置斜率k=-1的直线有多少条。
首先我们把每个格子都标上号。如图,我最多只能放置5条k=-1的直线。
当我枚举在1*1的方格中放置对角线的时候(当然此时斜率k=-1),一共可以放置9条对角线。
然而事实上斜率k=-1的对角线只需要5条就可以了,多出来了4条。那么是哪里多出来了这4条呢?
我们注意看所有2*2的方格,他们多有的右下角的那条都是多余的。如图:
很显然,我们需要删掉1245中的5,2356中的6,4578中的8,5689中的9,此时留下来的123475条直线可以代表所有斜率为-1的直线。
同理,当n!=m的时候,结果也是一样的。
再来考虑斜率不是-1的情况(当然我这里只是举了斜率为负的例子,斜率为正的情况和斜率为负的情况必定都是一样的,最后在结果*2就可以了。)
其实道理还是一样的,如果gcd(x,y)=1,可以保证这样的斜率是第一次出现,则我们只需要把每x*y格看成是1格就行了。
如果gcd(x,y)=2,这样的斜率是第二次出现了,这就类似于上面所说的2*2的情况了,对于所有这样的矩形,他的右下角部分都是多余的。
贴一下我的代码:
View Code
1 #include<cstdio> 2 int gcd[310][310]; 3 int Gcd(int a,int b) 4 { 5 return b == 0 ? a : Gcd(b,a%b); 6 } 7 int main() 8 { 9 for(int i = 1;i <= 300;i++) 10 for(int j = i;j <= 300;j++) 11 gcd[i][j] = Gcd(i,j); 12 int n,m; 13 long long int ans; 14 while(scanf("%d%d",&n,&m) == 2) 15 { 16 if(!n && !m) break; 17 n--;m--; 18 if(n > m) n ^= m,m ^= n,n ^= m; 19 ans = 0; 20 for(int x = 1;x <= n;x++) 21 { 22 for(int y = x;y <= m;y++) if(gcd[x][y] <= 2) 23 { 24 if(x == y) 25 { 26 int tmp = (n-x+1)*(m-y+1); 27 if(gcd[x][y] == 1) ans += tmp; 28 else ans -= tmp; 29 } 30 else 31 { 32 int tmp = (n-x+1)*(m-y+1); 33 if(x <= m && y <= n) tmp += (m-x+1)*(n-y+1); 34 if(gcd[x][y] == 1) ans += tmp; 35 else ans -= tmp; 36 } 37 } 38 } 39 ans *= 2; 40 printf("%lld\n",ans); 41 } 42 }