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

P3166 [CQOI2014]數三角形

傳送門

可以考慮容斥,用三個點的總方案減去三點共線的情況。總的點數為\(t=(n+1)*(m+1)\),那麼總方案數就是\(C_t^3\)

考慮三點共線,我們列舉這條線段的兩個端點\((a,b),(x,y)\),那麼這條線段上的整點數就是\(gcd(x-a,y-b)-1\)

然而這樣複雜度太高。我們考慮優化,把\((a,b)\)給移到原點,那麼\((x,y)\)變成\((x-a,y-a)\),我們可以考慮列舉這個記做\((i,j)\),然後發現有很多條線段都可以平移成這樣,總的條數為\((n-i+1)*(m-j+1)\)

以上只是斜率大於等於\(0\)的情況。不難發現每一條斜率小於\(0\)

的線段都和一條斜率大於\(0\)的一一對應。所以如果\((i,j)\)不在座標軸上我們還需要將總數乘上一個\(2\)

//minamoto
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n,m,gcd[1005][1005];ll ans,t;
int GCD(int a,int b){
    if(gcd[a][b])return gcd[a][b];
    if(!a)return gcd[a][b]=b;
    if(!b)return gcd[a][b]=a;
    return gcd[a][b]=GCD(b,a%b);
}
void init(){
    for(int i=1;i<=m;++i)gcd[0][i]=i;
    for(int i=1;i<=n;++i)gcd[i][0]=i;
    for(int i=1;i<=n;++i)for(int j=1;j<=m;++j)GCD(i,j);
}
int main(){
    scanf("%d%d",&n,&m),init();
    t=(n+1)*(m+1),ans=t*(t-1)*(t-2)/6;
    for(int i=0;i<=n;++i)for(int j=0;j<=m;++j)if(i||j){
        ans-=((!i||!j)?1ll:2ll)*(gcd[i][j]-1)*(n-i+1)*(m-j+1);
    }
    printf("%lld\n",ans);return 0;
}