[BZOJ3518] 点组计数

题面

显然

横着或竖着共有\(n*(_3^m)+m*(_3^n)\)

斜着的方案数为
\[ \sum_{i=1}^{n}\sum_{j=1}^{m}{(gcd(i,j)-1)(n-i)(m-j)} \]
就是枚举两个端点

令其中一个为原点

另一个为\((i,j)\)

\(gcd(i,j)=d\)

所以最靠近原点的整点是\((\frac{i}{d},\frac{j}{d})\)

所以从这个点到\((i,j)\)\(\frac{i}{(\frac{i}{d})}=d\)个整点

除去\((i,j)\)本身

那么他们之间有\(gcd(i,j)-1\)个整点

然后可以上下左右平移这个线段

答案就是\((gcd(i,j)-1)(n-i)(m-j)\)

考虑如何计算这个式子

\(-1\)提出来再反演即可
\[ \sum_{i=1}^{n}\sum_{j=1}^{m}{gcd(i,j)(n-i)(m-j)} \\=\sum_{i=1}^{n}\sum_{j=1}^{m}{\sum_{d|gcd(i,j)}{\varphi(d)}(n-i)(m-j)} \\=\sum_{d=1}^{n}\varphi(d)\sum_{i=1}^{\lfloor \frac{n}{d} \rfloor}{(n-i*d)}\sum_{j=1}^{\lfloor \frac{m}{d} \rfloor}{(m-j*d)} \\=\sum_{d=1}^{n}\varphi(d)*(n*\lfloor \frac{n}{d} \rfloor-d*\frac{\lfloor \frac{n}{d} \rfloor*(\lfloor \frac{n}{d} \rfloor-1)}{2})*(m*\lfloor \frac{m}{d} \rfloor-d*\frac{\lfloor \frac{m}{d} \rfloor*(\lfloor \frac{m}{d} \rfloor-1)}{2}) \]
因为提了一个$-1 $

答案先减去\(\frac{(n-1)*n}{2}+\frac{(m-1)*m}{2}\)

因为端点顺序不固定

答案乘\(2\)

最后再加上\(n*(_3^m)+m*(_3^n)\)即可

#include "bits/stdc++.h"

using namespace std;

#define ll long long

const int p=1e9+7;
const int N=1e6+7;

int tot;
int pri[N],phi[N];

inline void init(int n){
    phi[1]=1;
    for(int i=2;i<=n;++i){
        if(!phi[i])pri[++tot]=i,phi[i]=i-1;
        for(int j=1,tmp;j<=tot&&(tmp=i*pri[j])<=n;++j){
            if(i%pri[j]==0){
                phi[tmp]=phi[i]*pri[j];
                break;
            }
            phi[tmp]=phi[i]*phi[pri[j]];
        }
    }
}

inline ll s(ll x){
    return (x*(x+1)>>1)%p;
}

inline ll calc(ll n,ll d){
    return ((n/d*n-d*s(n/d))%p+p)%p;
}

inline ll C3(ll x){
    return (x<3?0:x*(x-1)*(x-2)/6)%p;
}

int main(){
    int n,m;
    scanf("%d %d",&n,&m);
    if(n>m)swap(n,m);
    ll ans=0;
    init(n);
    for(int d=1;d<=n;++d){
        (ans+=phi[d]*calc(n,d)%p*calc(m,d))%=p;;
    }
    (ans+=p-s(n-1)*s(m-1)%p)%p;
    printf("%lld\n",(ans*2%p+m*C3(n)%p+n*C3(m)%p)%p);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/yicongli/p/10165502.html