P3166[CQOI2014]数三角形
P3166[CQOI2014]数三角形
0x01 题意
给定一个\(N\times M\)的网格,请计算三点都在格点上的三角形共有多少个。注意三角形的三点不能共线。
0x02 解
简简单单写公式
\[ans=C_{(m+1)(n+1)}^3-(n+1)C_{m+1}^3-(m+1)C_{n+1}^3-斜线共线情况
\]
怎么处理斜线共线情况?
- \(O(n^2)\) 能过
枚举两点之间横坐标相差\(i\),纵坐标相差\(j\)
方案数为
\[\sum_{i=1}^n \sum_{j=1}^m (n-i+1)(m-j+1)(gcd(i,j)-1)
\]
其中\(gcd(i,j)-1\)是斜线穿过点的个数(不包括顶点)
- \(O(n)\) 神仙做法
0x03 代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m;
int ans=0,res=0;
int gcd(int a,int b){
return (b==0)?a:gcd(b,a%b);
}
signed main(){
cin>>n>>m;
ans+=(n*m+n+m+1)*(n*m+n+m)*(n*m+n+m-1)/6;
ans-=(m+1)*(n+1)*n*(n-1)/6;
ans-=(n+1)*(m+1)*m*(m-1)/6;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
res+=(n-i+1)*(m-j+1)*(gcd(i,j)-1);
cout<<ans-res*2;
return 0;
}
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1010;
int n,m;
int ans=0,res=0;
bool notp[N];
int cnt=0,phi[N],pri[N];
void getphi(int n){
cnt=0;
notp[1]=1,phi[1]=1;
for(int i=2;i<=n;i++){
if(!notp[i]) pri[++cnt]=i,phi[i]=i-1;
for(int j=1;j<=cnt&&pri[j]*i<=n;j++){
notp[pri[j]*i]=1;
if(i%pri[j]==0){phi[pri[j]*i]=phi[i]*pri[j];break;}
else phi[pri[j]*i]=phi[i]*(pri[j]-1);
}
}
}
signed main(){
cin>>n>>m;
ans+=(n*m+n+m+1)*(n*m+n+m)*(n*m+n+m-1)/6;
ans-=(m+1)*(n+1)*n*(n-1)/6;
ans-=(n+1)*(m+1)*m*(m-1)/6;
if(n>m) swap(n,m);
getphi(n);
for(int d=2;d<=n;d++)
res+=phi[d]*(n-d+n%d+2)*(n/d)*(m-d+m%d+2)*(m/d)/2;
cout<<ans-res;
return 0;
}