zoj3648 網格點銳角三角形方案數

題意:

求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;
}







發佈了52 篇原創文章 · 獲贊 9 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章