首页 > 技术文章 > Bzoj 3505: [Cqoi2014]数三角形 数论

Var123 2016-04-11 11:22 原文

Description

 

Input

输入一行,包含两个空格分隔的正整数m和n。

Output

输出一个正整数,为所求三角形数量。
 

Sample Input

输入1:

1 1

输入2:

2 2

Sample Output

输出1:

4

输出2:

76
 

Data Constraint

对于30%的数据 1<=m,n<=10

对于100%的数据 1<=m,n<=1000
 
题解:
数论
首先,我们可以将问题转化一下, 可以形成的三角形的个数=任选三个点的个数-三点共线个数
设总点数为k,则k=(n+1)*(m+1)
任选三个点的个数很简单:(k*(k-1)*(k-2))/6
至于三点共线个数,我们可以发现枚举每个点(i,j),则 (i,j) 和 (1,1) 再和 其他的一个点 能共线的个数为Gcd(i,j)-1,还有,因为我们枚举的点为(i,j),我们可以把(1,1)和(i,j)看为一个矩形,那么明显是可以在大的矩形中移动的。也就是我们算的线为(1,1)到(i,j)的线,但是其平行的线我们还要再算的,所以用 每个点的个数乘上(n-i+1)*(m-j+1)。然后我们把所有的点都计算一遍,然后把 和 乘2(因为我们刚才考虑的为从左下角往右上角偏的,不一定是平行,也就是和这个箭头差不多的方向↗️。我们还要考虑这种样子的↖️,显然两种的个数相等,所以要乘2。)最后,我们发现左右的线↔️和上下的线↕️没有减去,所以要单独减。
 
 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define LL long long 
 4 LL Gcd(int aa,int bb){if(bb==0)return aa;else return Gcd(bb,aa%bb);}
 5 int main()
 6 {
 7     LL n,m,k,k1,ans,i,j,tot=0;
 8     scanf("%lld %lld",&n,&m);
 9     k=(n+1)*(m+1);
10     ans=k*(k-1)*(k-2)/6;
11     for(i=1;i<=n;i++)
12     {
13         for(j=1;j<=m;j++)
14         {
15             k=Gcd(i,j);
16             if(k>=2)tot+=(k-1)*(n-i+1)*(m-j+1);
17         }
18     }
19     n++;m++;
20     k=m*(m-1)*(m-2)/6;
21     k1=n*(n-1)*(n-2)/6;
22     ans-=(n*k+m*k1);
23     printf("%lld",ans-tot*2);
24     fclose(stdin);
25     fclose(stdout);
26     return 0;
27 }

 

推荐阅读