1. 程式人生 > >[CQOI2014]數三角形

[CQOI2014]數三角形

pan class spa 傳送門 兩種 一個 不能 給定 for

傳送門

題意:給定一個nxm的網格,計算三點都在格點上的三角形共有多少個?註意三角形的三點不能共線.

分析:nxm的網格有tot=(n+1)*(m+1)個格點,暴力算出所有方案數\(C_{tot}^3\),然後減去平行於x軸和y軸的共線三角形\((n+1)*C_{m+1}^3\)\((m+1)*C_{n+1}^3\),最後減去傾斜直線上的共線三角形.

這個比較麻煩,我先直接給出式子,然後再來具體分析,因為懶得畫圖(其實是不會),所以就只能耐心地口胡\(2*(gcd(i,j)-1)*(n+1-i)*(m+1-j)\)

乘2:矩形具有對稱性,所以傾斜直線也是對稱的(就是說把傾斜直線看作一種是自左上往右下,另一種是自右上往左下,這兩種情況是相同的,所以我們下面可以只討論一種情況)

乘gcd(i,j)-1:首先要知道一個結論,對於點(a,b)和(x,y)連成的線段而言(其中a>x,b>y),在它們中間有gcd(a-x,b-y)-1個整點(自己畫幾個圖就能發現這個規律了),然後我們不妨以左上角(0,0)點為枚舉矩形的左上角頂點,所以我們只需要枚舉右下角頂點(i,j)就可以了.根據結論這兩個點連成的線段中間有gcd(i-0,j-0)-1個格點.

對於\(n*m\)的網格而言,像上述那樣枚舉矩形的話,一共有(n+1-i)*(m+1-j)個矩形(這裏不明白也畫幾個圖吧)

int gcd(int x,int y){
    if(y==0)return x;
    return gcd(y,x%y); 
}
int main(){
    int m=read(),n=read();
    m++;n++;//為了方便,這裏直接都+1了
    int tot=m*n;
    long long ans=1LL*tot*(tot-1)*(tot-2)/6-1LL*m*n*(n-1)*(n-2)/6-1LL*n*m*(m-1)*(m-2)/6;
    for(int i=1;i<n;i++)
        for(int j=1;j<m;j++)
            ans-=1LL*2*(gcd(i,j)-1)*1LL*(n-i)*(m-j); 
    printf("%lld\n",ans);
    return 0;
}

[CQOI2014]數三角形