題意:
求N x M的矩形網格中有多少個以格點爲頂點的銳角三角形。1<=n,m<=100
解法:
原文鏈接:http://www.cnblogs.com/jffifa/archive/2012/10/02/2710360.html
首先注意到任意一個三角形可以唯一確定一個包含它的最小矩形,並且三角形至少有一個頂點在矩形的頂點上。
然後可以發現,對於任意的銳角三角形,三個頂點一定都在矩形的邊上。
如果我們知道給定大小的矩形上有多少個銳角三角形,我們就可以枚舉矩形大小,算出大矩形內有多少個小矩形,乘上小矩形上的銳角三角形個數,最後再求和就可以了。
現在問題就是求給定大小的矩形上有多少個銳角三角形。設矩形大小爲(i, j),不妨令三角形的一個頂點在(i, j)上,如圖。
現在要求alpha和beta都是銳角,用向量點積或者餘弦定理都可以解出q*j>p*(i-p)以及p*i>q*(j-q),且0<p<=i以及0<q<j。枚舉p的話,q的解集是可以O(1)算出來的(一個一次不等式解集和一個二次不等式解集的交)。頂點在其它三個點的情況類似。
這樣對於給定的(i, j),銳角三角形的個數就可以O(N)求出。所以對於所有的(i, j),只需要O(N^3)時間預處理統計。
具體實現時候,我用了當p遞增時關於q的二次不等式解集的兩個邊界一增一減的性質,這樣可以避免浮點運算和一大堆亂七八糟的討論。
注:看完題解自己敲了一遍,在不等式求解的部分各種出錯。
1.關於p,q的取值。 0<p<=i,0<q<j
p只取了右側等號,相當於左圖的情況 cal( i , j )。 這樣右圖的情況可以用 cal( j , i ) 來計算
矩形大小爲 i, j 時,方案數爲 2 * (cal( i , j )+cal( j , i ))
2. 二次不等式求解 p*i>q*(j-q)
p增大的過程中不斷壓縮L,R的大小
while (l<j && l<=r && p*i>l*(j-l)) l++;
while (r>0 && l<=r && p*i>r*(j-r)) r- -;
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cstring>
#include <cmath>
#include <set>
#include <vector>
using namespace std;
typedef long long LL;
LL f[200][200];
LL cal(int i,int j)
{
LL res=0;
int l = 0, r = j;
for (int p = 1; p <= i; ++p)
{
//二次不等式求解
while (l<j && l<=r && p*i>l*(j-l)) l++;
while (r>0 && l<=r && p*i>r*(j-r)) r--;
//不等式求交
int q = p*(i-p)/j;
if (q >= r)
res += max(0, j-q-1);
else if (q >= l)
res += max(0, j-r-1);
else
res += max(0, j-r-1)+max(0, l-q-1);
}
return res;
}
int main()
{
int n,m;
for(int i=1; i<=100; i++)
{
for(int j=1; j<=100; j++)
f[i][j]=(cal(i,j)+cal(j,i))*2;
//0<p<=i 決定了cal(i,j)和cal(j,i)不同
}
while(scanf("%d %d",&n,&m)!=EOF)
{
LL ans=0;
for(int i=1; i<=n; i++)
{
for(int j=1; j<=m; j++)
ans+=f[i][j]*(n-i+1)*(m-j+1);
}
printf("%lld\n",ans);
}
return 0;
}
另附:zoj 3647
題意:
求N x M的矩形網格中有多少個以格點爲頂點的三角形。1<=n,m<=1000
解題思路:
C( (N+1)*(M+1) , 3) - (N+1) * C(M+1,3) - (M+1) * C(N+1 , 3) - 三點在同一條斜線上的選法
每個斜線段唯一對應一個矩形的對角線,固定兩點爲對角線端點,剩下一個點的選法爲 gcd( N , M ) - 1
大小爲 i, j 的矩形 一共有 (N - i + 1 ) * ( M - j + 1 ) 個
#include <algorithm>
#include <cstring>
#include <cmath>
#include <cstdio>
#include <vector>
using namespace std;
typedef long long LL;
LL gcd(LL a,LL b)
{
if(b==0)
return a;
return gcd(b,a%b);
}
LL cal(LL n)
{
if(n<3) return 0;
return n*(n-1)*(n-2)/6;
}
int main()
{
LL n,m;
while(scanf("%lld %lld",&n,&m)!=EOF)
{
LL ans=cal((n+1)*(m+1))-(n+1)*cal(m+1)-(m+1)*cal(n+1);
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
ans-=2*(n-i+1)*(m-j+1)*(gcd(i,j)-1);
}
}
printf("%lld\n",ans);
}
return 0;
}