UVALive 3295
题目大意:见刘汝佳《算法竞赛入门经典——训练指南》P173
解题思路:
每一个合法的三角形的三个顶点都不在同一直线上,那么问题其实就是在求所有不全在同一直线上的三点的组合数。
我们可以利用容斥原理,先求出所有的三个顶点的组合数C[(n+1)*(m+1)][3]。全在同一直线上的三个网格顶点有三种:三点在同一水平线上的,三点在同一竖直线上的,三点在同一斜线上的。前两种不难求,因此不再赘述,这里重点讲解第三种。
设三点坐标为(x1,y1),(x2,y2),(x3,y3),且x1 < x2 < x3,y1 < y2 < y3,设 x2-x1 = i,x3-x2 = j,当且仅当 i*(y3-y2) = j*(y2-y1)时,三点在同一斜线上。那么我们可以枚举 i 和 j ,求出 g = gcd(i,j),对于每一个(i,j),再从 1 开始枚举 k ,则此时 y3-y2 = k*j/g,y2-y1 = k*i/g(当 y3 - y1 >= m+1时退出循环),这样就可以知道 x3-x1 和 y3-y1 的值了,接下来就只需要求出放得下多少条这样的斜线就可以。
AC代码:
1 #include <iostream> 2 #include <cstdio> 3 #include <algorithm> 4 5 using namespace std; 6 typedef long long ll; 7 const int maxn=1002001+3; 8 ll C[maxn][5]; 9 void init(){ 10 C[0][0]=1; 11 C[1][1]=C[1][0]=1; 12 for(int n=2;n<maxn;n++){ 13 C[n][0]=1; 14 if(n<=3) C[n][n]=1; 15 for(int m=1;m<min(4,n);m++){ 16 C[n][m]=C[n-1][m-1]+C[n-1][m]; 17 } 18 } 19 } 20 int gcd(int a,int b){ 21 if(b==0) return a; 22 return gcd(b,a%b); 23 } 24 int main(){ 25 init(); 26 int n,m; 27 int kase=1; 28 while(scanf("%d%d",&n,&m)==2&&n&&m){ 29 n++,m++; 30 if(n>m) swap(n,m); 31 ll ans=C[n*m][3]; 32 ans-=C[n][3]*m; 33 ans-=C[m][3]*n; 34 ll t=0; 35 for(int i=1;i<n;i++){ 36 for(int j=1;i+j<n;j++){ 37 int gd=gcd(i,j); 38 for(int k=1;;k++){ 39 int id=k*j/gd,ij=k*i/gd; 40 if(id+ij>=m) break; 41 t+=(n-(i+j))*(m-(k*(i+j)/gd)); 42 } 43 } 44 } 45 ans-=t*2; 46 printf("Case %d: %lld\n",kase++,ans); 47 } 48 return 0; 49 }
“这些年我一直提醒自己一件事情,千万不要自己感动自己。大部分人看似的努力,不过是愚蠢导致的。什么熬夜看书到天亮,连续几天只睡几小时,多久没放假了,如果这些东西也值得夸耀,那么富士康流水线上任何一个人都比你努力多了。人难免天生有自怜的情绪,唯有时刻保持清醒,才能看清真正的价值在哪里。”