点组计数

Time Limit: 20 Sec Memory Limit: 128 MB

Description

平面上摆放着一个nm的点阵(下图所示是一个34的点阵)。Curimit想知道有多少三点组(a,b,c)满足以a,b,c三点共线。这里a,b,c是不同的3个点,其顺序无关紧要。(即(a,b,c)和(b,c,a)被认为是相同的)。由于答案很大,故你只需要输出答案对1,000,000,007的余数就可以了。

img

Input

有且仅有一行,两个用空格隔开的整数n和m。

Output

有且仅有一行,一个整数,表示三点组的数目对1,000,000,007的余数。(1,000。000。007是质数)

Sample Input

3 4

Sample Output

2 0

HINT

对于100%的数据,1< =N.m< =50000

Main idea

给定一个点阵,问有多少组三点共线。

Solution

img

其实我也不知道原式怎么来的,我可能只会推式子啊?QAQ

Code

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
#include<bits/stdc++.h>
using namespace std;
typedef long long s64;

const int ONE = 50005;
const int MOD = 1000000007;
const int Ny6 = 166666668;

int T;
int n,m;
bool isp[ONE];
int prime[ONE],p_num;
int phi[ONE];
s64 Ans;

int get()
{
int res=1,Q=1; char c;
while( (c=getchar())<48 || c>57)
if(c=='-')Q=-1;
if(Q) res=c-48;
while((c=getchar())>=48 && c<=57)
res=res*10+c-48;
return res*Q;
}

void Getphi(int MaxN)
{
phi[1] = 1;
for(int i=2; i<=MaxN; i++)
{
if(!isp[i])
prime[++p_num] = i, phi[i] = i - 1;
for(int j=1; j<=p_num, i*prime[j]<=MaxN; j++)
{
isp[i * prime[j]] = 1;
if(i % prime[j] == 0)
{
phi[i * prime[j]] = (s64)phi[i] * prime[j] % MOD;
break;
}
phi[i * prime[j]] = (s64)phi[i] * phi[prime[j]] % MOD;
}
}
}

s64 Get(int a,int b,int num) {return (s64)(a+b) * num / 2 %MOD; }
s64 Sum(int n,int m) {return ((s64)n*(n-1)/2%MOD) * ((s64)m*(m-1)/2%MOD) % MOD;}

int C(int n)
{
int res = (s64)n * (n-1) % MOD * (n-2) % MOD;
return (s64) res * Ny6 % MOD;
}

int main()
{
Getphi(ONE-1);
n=get(); m=get();
if(n > m) swap(n,m);
for(int d=1; d<=n; d++)
{
Ans += phi[d] * Get(n-d,n-(n/d)*d,n/d) % MOD *Get(m-d,m-(m/d)*d,m/d) % MOD;
Ans %= MOD;
}

Ans = (Ans - Sum(n,m) + MOD) % MOD;
Ans = Ans*2%MOD + (s64)C(n)*m%MOD + (s64)C(m)*n%MOD;
Ans %= MOD;

printf("%lld",Ans);
}