zoukankan      html  css  js  c++  java
  • Hihocoder1456 Rikka with Lattice

    众所周知,萌萌哒六花不擅长数学,所以勇太给了她一些数学问题做练习,其中有一道是这样的:
    勇太有一个$n times m$的点阵,他想要从这$n times m$个点中选出三个点 ${A,B,C}$,满足:

    1. 三角形$ABC$面积不为$0$且其内部不存在整点。
    2. 边$AB$,$BC$,$CA$上不存在除了端点以外的整点。

    现在勇太想要知道有多少种不同的选取方案满足条件。
    当然,这个问题对于萌萌哒六花来说实在是太难了,你可以帮帮她吗?
    注意${A,B,C}$与${B,A,C}$视为同一种方案。
    $n,m leqslant 5 times 10^9$。

    题解

    一道比较有趣的数论题。看范围应该能猜到是杜教筛一类的东西,但是推不出第一步的式子啊= =
    首先考虑题目这个奇怪的条件,其实由皮克定理,我们可以得到,这个三角形的面积为$frac{1}{2}$,也就是说,题目要求的就是面积为$frac{1}{2}$的三角形的个数。
    考虑这种三角形大概长什么样子,然后你会发现它只能长成这样:

    考虑一个$a times b$的矩形,以这个矩形的两个相对的点为其中两个顶点的三角形的个数。设$vec{BE}$的坐标为$(x,y)$那么这个三角形的面积就可以表示为:
    $$S=frac{1}{2}|vec{BE} times vec{BD}|=frac{1}{2}|ay-bx|$$
    令$S=frac{1}{2}$,则有$ay-bx=pm 1$。
    由裴蜀原理,这个方程仅在$gcd(a,b)=1$时有解,且每个方程恰好有一组解。再把对角线换一下,于是当$gcd(a,b)=1$时,会有$4$个这样的三角形。
    然后这个奇怪的题,终于被我们化成了这样的式子:
    $$sum_{i=1}^{n}sum_{j=1}^{m}[gcd(i,j)=1]4(n-i)(m-j)$$
    莫比乌斯反演一下,就变成这样了:
    $$4 sum_{x=1}^{min(n,m)} mu(x) sum_{i=1}^{lfloor frac{n}{x} rfloor} sum_{j=1}^{lfloor frac{m}{x} rfloor} nm - mix - njx + ijx^2$$
    令$S(n)=sum_{i=1}^{n}i=frac{n(n+1)}{2}$把式子再化简一下,就变成:
    $$4 sum_{x=1}^{min(n,m)} mu(x)(lfloor frac{n}{x} rfloor lfloor frac{m}{x} rfloor nm-x lfloor frac{m}{x} rfloor S(lfloor frac{n}{x} rfloor)-x lfloor frac{n}{x} rfloor S(lfloor frac{m}{x} rfloor)+x^2 S(lfloor frac{m}{x} rfloor) S(lfloor frac{n}{x} rfloor))$$
    终于,这个式子的求法很显然了,可以直接枚举,$O(n)$计算。然而这还是过不了,得加上杜教筛。
    这里杜教筛要筛的东西还挺多的,一个是$sum_{i=1}^{n}mu(i)$,一个是$sum_{i=1}^{n}mu(i)i$,还要筛出$sum_{i=1}^{n}mu(i)i^2$。
    关于这三个函数的筛法,我都在这里讲一下:
    关于杜教筛,我们知道有个这样的式子:
    $$L(n)-g(1)F(n)=sum_{i=2}^n g(i) F(lfloorfrac{n}{i}rfloor)$$
    其中$f*g=l$。
    对于$sum_{i=1}^{n}mu(i)$,我们是令$g=1$,利用$sum_{d|n}mu(d)=[n=1]$,于是使得$L(n)=1$,从而完成计算;
    对于$sum_{i=1}^{n}mu(i)i$,我们则令$g(i)=i$,那么$sum_{d|n}mu(d)d times frac{n}{d}=1$,从而$L(n)=1$,从而实现了杜教筛;
    对于$sum_{i=1}^{n}mu(i)i^2$,类似地,我们令$g(i)=i^2$,那么$sum_{d|n}mu(d)d^2 times (frac{n}{d})^2=1$,从而$L(n)=1$,从而也实现了杜教筛;
    由于$5 times 10^9 times 5 times 10^9=2.5 times 10^{19}$超过了long long的范围,因此取模变得很恶心,时常要记得取模。
    偷懒用了std::map,所以时间复杂度$O(n^{frac{2}{3}}logn)$。

    代码

    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
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    #include<cmath>
    #include<bitset>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    using namespace std;
    typedef long long ll;
    const int INF=1e9;
    const long double eps=1e-6;
    const int mod=998244353;
    const int maxn=4e6+10;
    大专栏  Hihocoder1456 Rikka with Lattice class="line">const int Inv2=499122177;
    const int Inv6=166374059;
    int Lim=4e6;
    ll mu[maxn],mux[maxn],muxx[maxn];
    int prime[maxn];
    map <ll,ll> Mu,Mux,Muxx;
    bitset <maxn> vis;
    inline ll (){
    ll x=0,flag=1;
    char ch=getchar();
    while(!isdigit(ch) && ch!='-')ch=getchar();
    if(ch=='-')flag=-1,ch=getchar();
    while(isdigit(ch))x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
    return x*flag;
    }
    inline void initialize(int n){
    int i,j,cnt=0;
    vis[1]=1;mu[1]=1;
    for(i=2;i<=n;i++){
    if(!vis[i]){
    prime[++cnt]=i;
    mu[i]=-1;
    }
    for(j=1;j<=cnt && i*prime[j]<=n;j++){
    vis[i*prime[j]]=1;
    mu[i*prime[j]]=-mu[i];
    if(i%prime[j]==0){
    mu[i*prime[j]]=0;
    break;
    }
    }
    }
    for(i=1;i<=n;i++){
    mux[i]=(mux[i-1]+mu[i]*i)%mod;
    muxx[i]=(muxx[i-1]+mu[i]*i%mod*i)%mod;
    mu[i]=mu[i-1]+mu[i];
    }
    }
    ll Get_Mu(ll x){
    if(x<=Lim)return mu[x];
    if(Mu[x])return Mu[x];
    ll res=1,i;
    for(i=2;i<=x;i++){
    ll j=x/(x/i);
    (res-=Get_Mu(x/i)*((j-i+1)%mod))%=mod;i=j;
    }
    return Mu[x]=res;
    }
    inline ll Sum1(ll x){
    x%=mod;
    return x*(x+1)%mod*Inv2%mod;
    }
    ll Get_Mux(ll x){
    if(x<=Lim)return mux[x];
    if(Mux[x])return Mux[x];
    ll res=1,i;
    for(i=2;i<=x;i++){
    ll j=x/(x/i);
    (res-=Get_Mux(x/i)*(Sum1(j)-Sum1(i-1)))%=mod;i=j;
    }
    return Mux[x]=res;
    }
    inline ll Sum2(ll x){
    x%=mod;
    return x*(x+1)%mod*(2*x+1)%mod*Inv6%mod;
    }
    ll Get_Muxx(ll x){
    if(x<=Lim)return muxx[x];
    if(Muxx[x])return Muxx[x];
    ll res=1,i;
    for(i=2;i<=x;i++){
    ll j=x/(x/i);
    (res-=Get_Muxx(x/i)*(Sum2(j)-Sum2(i-1)))%=mod;i=j;
    }
    return Muxx[x]=res;
    }
    int main(){
    ll m,n,d;
    #ifndef ONLINE_JUDGE
    freopen("hiho1456.in","r",stdin);
    freopen("hiho1456.out","w",stdout);
    #endif
    n=read();m=read();
    if(n>m)swap(n,m);
    initialize(Lim);
    ll ans=0;
    for(d=1;d<=n;d++){
    ll j=min(n/(n/d),m/(m/d));
    ll v1=(n/d)%mod,v2=(m/d)%mod;
    ll Sum1=v1*(v1+1)%mod*Inv2%mod,Sum2=v2*(v2+1)%mod*Inv2%mod;
    (ans+=1ll*(Get_Mu(j)-Get_Mu(d-1))*v1%mod*v2%mod*n%mod*m%mod)%=mod;
    (ans+=1ll*(Get_Muxx(j)-Get_Muxx(d-1))*Sum1%mod*Sum2%mod)%=mod;
    (ans-=1ll*(Get_Mux(j)-Get_Mux(d-1))*Sum1%mod*m%mod*v2%mod)%=mod;
    (ans-=1ll*(Get_Mux(j)-Get_Mux(d-1))*Sum2%mod*n%mod*v1%mod)%=mod;
    d=j;
    }
    printf("%lldn",(ans+mod)%mod*4ll%mod);
    return 0;
    }
  • 相关阅读:
    Md5
    hdu 2569 彼岸
    调用系统相机相冊
    白盒測试
    HDU 1501
    IOS常见错误分析解决(一直更新) 你值得收藏-综合贴
    读“程序猿生存定律”笔记
    Halcon导出的cpp, VC++环境配置
    POJ 1260 Pearls (动规)
    hdoj-1856-More is better【并查集】
  • 原文地址:https://www.cnblogs.com/lijianming180/p/12286198.html
Copyright © 2011-2022 走看看