• 2020寒假专题训练:数论


    Day1 20.02.11

    一、素数判定:Miller-Rabbin

    ①费马小定理:$a^{p-1} equiv 1 pmod p$($p$为素数).

    因此可以选取若干个 $a$,对 $p$ 进行判定.  满足条件的 $p$ 为素数的概率在 $frac{3}{4}$ 左右.

    ②二次探测定理:$x^2 equiv 1 pmod p$ 当 $p$ 为大于 $2$ 的素数时仅有两个解 $x_1=1,x_2=p-1$.

    因此考虑选取 $9$ 至 $12$ 个素数,先使用费马小定理判定,再对 $a^{frac{p-1}{2^k}}$ 使用二次探测判定.

    代码如下:

    inline long long mul ( long long x,long long y,long long mod ) { return (long long)((__int128)x*y%mod); }
    inline long long power ( long long x,long long y,long long mod )
    {
        long long z=1;
        for ( ;y;y>>=1,x=mul(x,x,mod) ) if ( y&1 ) z=mul(z,x,mod);
        return z;
    }
    const long long Prime[13]={1,2,3,5,7,11,13,17,19,61,2333,4567,24251};
    long long Pow[100],ans;
    inline bool Miller_Rabbin ( long long p )
    {
        if ( p==1 ) return false;
        for ( int i=1;i<=12;i++ )
        {
            if ( p==Prime[i] ) return true;
            if ( !(p%Prime[i]) or power(Prime[i]%p,p-1,p)!=1 ) return false;
            long long x=p-1;
            while ( !(x&1) )
            {
                x>>=1;
                long long res=power(Prime[i]%p,x,p);
                if ( res!=1 and res!=p-1 ) return false;
                if ( res==p-1 ) break;
            }
        }
        return true;
    }
    Miller_Rabbin

    二、素数筛法:线性筛(欧拉筛)

    ①埃氏筛:枚举每个素数 $p$,将 $p$ 的倍数标记为非素数. 可以证明效率为 $O(n log log n)$.

    ②欧拉筛:考虑对埃氏筛进行优化. 不难发现,埃氏筛的问题在于对多因子的合数进行了多次标记.

    考虑只对每个数标记一次. 令每个数只被其最小的素因子筛到. 考虑转化为枚举除去最小素因子的剩下的因子.

    对于每个数枚举小于其的素数. 将这两个数的积标记为非素数. 当枚举的素数是当前数的因子是退出循环即可.

    效率 $O(n)$. 代码如下:

    bool flag[100000000];
    std::vector<int> pr;
    inline void solve ( int n )
    {
        for ( int i=2;i<=n;i++ )
        {
            if ( !flag[i] ) pr.push_back(i);
            for ( int j:pr )
            {
                if ( i*j>n ) break;
                flag[i*j]=true;
                if ( !(i%j) ) break;
            }
        }
    }
    线性筛(欧拉筛)

    线性筛还可以用来求积性函数. 后文 Day4 部分有提及.

    三、质因数分解:Pollard-Rho

    前置知识:素数判定 Miller-Rabbin.

    引理:生日悖论:$23$ 人中,存在两个人在同一天生日的概率超过 $frac{1}{2}$.

    考虑找到一个数 $a$ 使得 $1 < gcd(a,n) < n$. 则有:$gcd(a,n) mid n, frac{n}{gcd(a,n)} mid n$.

    考虑用随机的方式找到 $a$. 构造函数 $f(x)=(x^2+c) mod n$. 其中 $c$ 为一个固定的随机数.

    先任意取 $x_1$,然后令 $x_i=f(x_{i-1})(i geq 2)$ 即可.

    观察生成的随机数列,发现分布较为均匀.

    但注意到一个问题:生成的数列在一定长度后会陷入循环,形成 $ ho$ 形数列.

    因此每次判断 $d=gcd(lvert x_i-x_1 vert,n)$ 是否符合要求. 当 $x_i=x_1$ 时退出循环. 由引理知这样一个环的期望长度为 $O(sqrt n)$.

    考虑对求解的过程进行优化. 由于计算 $gcd$ 的时间较慢,考虑每隔一段时间统一计算. 采用倍增的思想,统计 $res=(prod lvert x_i - x_1  vert) mod n$.

    当 $res=0$ 时出现环. 每隔 $2^k$ 次计算一次 $gcd$,重新初始化即可. 代码如下:

     1 inline long long work ( long long x )
     2 {
     3     long long s=0,t=0,c=1LL*rand()*rand()*rand()%(x-1)+1;
     4     for ( int step=1;;step<<=1,s=t )
     5     {
     6         long long res=1;
     7         for ( int i=1;i<=step;i++ )
     8         {
     9             t=(mul(t,t,x)+c)%x;
    10             res=mul(res,std::abs(t-s),x);
    11             if ( !(i%127) )
    12             {
    13                 long long d=std::__gcd(res,x);
    14                 if ( d!=1 ) return d;
    15             }
    16         }
    17         long long d=std::__gcd(res,x);
    18         if ( d!=1 ) return d;
    19     }
    20     return -1;
    21 }
    22 inline void Pollard_Rho ( long long x )
    23 {
    24     if ( x==1 or x<=ans ) return;
    25     if ( Miller_Rabbin(x) ) { ans=std::max(ans,x);return; }
    26     long long y=x;
    27     while ( y>=x ) y=work(x);
    28     while ( !(x%y) ) x/=y;
    29     Pollard_Rho(x);Pollard_Rho(y);
    30 }
    Pollard-Rho

    四、BSGS/exBSGS

    1. BSGS

    求解方程:$y^x equiv z pmod p$($p$ 为素数).

    令 $x=am-b(0 leq b leq m-1)$,则有:$y^{am} equiv z imes y^b pmod p$.

    等式右边有 $m$ 种取值,用 hash_table 或 unordered_map 记录.

    等式左边枚举 $a$,查询右边是否出现过该值即可.

    当 $m=sqrt p$ 时效率最优,效率为 $O(sqrt p)$.

    实际写代码时由于数据原因,常将 $m$ 与一个常量(例如 $1000$,视数据而定)取 $min$.

    代码如下:

     1 std::unordered_map<int,int> map;
     2 inline int BSGS ( int y,int z,int p )
     3 {
     4     int m=(int)sqrt(p)+1;
     5     if ( z%p==1 ) return 0;
     6     if ( !(y%p) ) return -1;
     7     map.clear();
     8     for ( int i=0,t=z;i<m;i++,t=1LL*t*y%p ) map[t]=i;
     9     for ( int i=1,v=power(y,m,p),t=v;i<=m+1;i++,t=1LL*t*v%p ) if ( map.count(t) ) return i*m-map[t];
    10     return -1;
    11 }
    BSGS

    2. exBSGS

    求解方程:$y^x equiv z pmod p$($p$ 为任意正整数).

    将等式展开有:$y imes y^{x-1} + k imes p = z(k in mathbb{Z})$.

    由裴属定理(Day2 内容)知,当且仅当 $d=gcd(y,k) mid z$ 时有解.

    若有解则有:$frac{y}{d}  imes y^{x-1} + k imes frac{p}{d} = z$.

    不断递归求解,最终得到 $gcd(y,p)=1$ 时即可用 BSGS 求解.

    设递归 $c$ 次,$g=prodlimits_{i=1}^{c} d_i$.

    则有:$y^{x-c} imes frac{y^c}{g} equiv frac{z}{g} pmod {frac{p}{g}}$.

    代码如下:

     1 std::unordered_map<int,int> map;
     2 inline int exBSGS ( int y,int p,int z )
     3 {
     4     if ( z==1 ) return 0;
     5     int c=0,g=1;
     6     for ( int d=std::__gcd(y,p);d!=1;d=std::__gcd(y,p) )
     7     {
     8         if ( z%d ) return -1;
     9         c++;z/=d;p/=d;g=1LL*g*y/d%p;
    10         if ( z==g ) return c;
    11     }
    12     int m=sqrt(p)+1;map.clear();
    13     for ( int i=0,t=z;i<m;i++,t=1LL*t*y%p ) map[t]=i;
    14     for ( int i=1,v=power(y,m,p),t=1LL*g*v%p;i<=m+1;i++,t=1LL*t*v%p ) if ( map.count(t) ) return i*m-map[t]+c;
    15     return -1;
    16 }
    exBSGS


    Day2 20.02.12

    一、裴蜀定理

    裴蜀定理:方程 $sumlimits_{i=1}^{n} a_i imes x_i = b$ 有整数解的充要条件为 $gcd(a_1,a_2,dots,a_n mid b$.

    证明:

    ①必要性:

    令 $d=gcd(a_1,a_2,dots,a_n)$,则 $forall i in [1,n], d mid a_i$.

    $ herefore d mid sumlimits_{i=1}^{n} a_i imes x_i$,即 $gcd(a_1,a_2,dots,a_n) mid b$.

    ②充分性:

    考虑数学归纳法.

    Ⅰ. $n=2$ 时成立(详见 exgcd 部分).

    Ⅱ. 假设 $n=k$ 时成立,考虑 $n=k+1$ 时,

    设 $S=sumlimits_{i=1}{k} a_i,d=gcd(a_1,a_2,dots,a_k)$.

    则方程 $S imes X + a_{k+1} imes y = gcd(d,a_{k+1})$ 有整数解.

    即存在 $(X,Y)$ 使得 $sumlimits_{i=1}^{k+1} a_i imes x_i = gcd(a_1,a_2,dots,a_{k+1})$ 一组解为 $x_i=X(i in [1,k]),x_{k+1}=Y$.

    $ herefore n=k+1$ 时成立.

    综上,证毕.

    二、欧拉定理

    欧拉定理:$a^{varphi(m)} equiv 1 pmod m(a perp m)$.

    证明:考虑所有与 $m$ 互素的数 $x_i(i in [1,varphi(m)]$,令 $p_i=ax_i$,不难得到 ${p_1 mod m,p_2 mod m,dots,p_{varphi(m)} mod m}$ 与 ${x_1,x_2,dots,x_{varphi(m)}$ 一一对应.

    累乘得:$prodlimits_{i=1}^{varphi(m)} p_i =prodlimits_{i=1}^{varphi(m)} x_i $,则 $a^{varphi(m)} equiv 1 pmod m$.

    扩展欧拉定理:当 $b geq varphi(m)$ 时,$a^b equiv a^{b mod varphi(m) + varphi(m) pmod m$.

    证明考虑 $m$ 的每个素因子即可,此处不予给出.

    三、exgcd

    求解方程:$ax+by=c$ 或 $ax equiv c pmod b$.

    当 $gcd(a,b) mid c$ 时原方程有解. 考虑先构造出方程 $ax+by=gcd(a,b)$ 的解,再令 $x'=frac{c}{gcd(a,b)} imes x$ 即可,$y$ 同理.

    设:$egin{cases}ax_1+by_1=gcd(a,b)\bx_2+(amod b)y_2=gcd(b,amod b)end{cases}$.

    又 $gcd(a,b)=gcd(b,a mod b)$ 得,$ax_1+by_1=bx_2+(a mod b)y_2$.

    将 $amod b$ 展开并整理得:$ax_1+by_1=ay_2+b(x_2-lfloor frac{a}{b} floor y_2)$.

    不断递归处理即可. 边界条件为当 $b=0$ 时,$x=1,y=0$. 代码如下:

    1 inline void exgcd ( int a,int b,int &x,int &y )
    2 {
    3     if ( !b ) { x=1;y=0;return; }
    4     exgcd(b,a%b,y,x);y-=a/b*x;
    5 }
    exgcd

    四、CRT/exCRT

    求解方程组:$egin{cases}xequiv a_1 pmod {n_1}\xequiv a_2 pmod {n_2}\ dots \xequiv a_k pmod {n_k}end{cases}$. 其中 $forall i eq j, n_i perp n_j$.

    CRT(中国剩余定理):

    ① 令 $n=prodlimits_{i=1}^{k} n_i$.

    ② 对于第 $i$ 个方程:

    Ⅰ. 计算 $m_i=frac{n}{n_i}$.

    Ⅱ. 计算 $m_i$ 在 $mod n_i$ 意义下的逆元 $m_i^{-1}$. 即方程 $am_i+bn_i=1$ 的最小正整数解 $a$,可使用 exgcd 求解.

    Ⅲ. 令 $c_i=m_im_i^{-1}$(不对 $n_i$ 取模).

    ③ 则唯一解为 $x equiv sumlimits_{i=1}^{k} a_ic_i pmod n$.

    证明直接代入即可. 代码如下:

     1 inline long long CRT ( int n,long long *a,long long *m )
     2 {
     3     long long M=1,res=0;
     4     for ( int i=1;i<=n;i++ ) M*=m[i];
     5     for ( int i=1;i<=n;i++ )
     6     {
     7         long long w=M/m[i],x,y;exgcd(w,m[i],x,y);x=(x%m[i]+m[i])%m[i];
     8         res=(res+mul(mul(a[i],w,M),x,M)+M)%M;
     9     }
    10     return res;
    11 }
    CRT

    求解方程组:$egin{cases}xequiv a_1 pmod {n_1}\xequiv a_2 pmod {n_2}\ dots \xequiv a_k pmod {n_k}end{cases}$. 其中 $n_i$ 为任意正整数.

    exCRT:

    考虑 $egin{cases} xequiv a_1 pmod {n_1}\xequiv a_2 pmod {n_2} end{cases}$. 有:$egin{cases} x=pn_1+a_1 \ x=qn_2+a_2 end{cases}$.

    则 $n_1p-n_2q=a_2-a_1$. 用 exgcd 求出一组 $p,q$,则有:$x equiv n_1p+a_1 pmod {operatorname{lcm} (n_1,n_2)}$. 两两合并即可. 

    代码如下:

     1 inline long long exCRT ( void )
     2 {
     3     long long m=a[1],ans=r[1],x=0,y=0;
     4     for ( int i=1;i<=n;i++ )
     5     {
     6         long long B=((r[i]-ans)%a[i]+a[i])%a[i],gcd=exgcd(m,a[i],x,y);
     7         x=mul(x,B/gcd,a[i]);ans+=m*x;m*=a[i]/gcd;ans=(ans+m)%m;
     8     }
     9     return (ans%m+m)%m;
    10 }
    exCRT

    五、Lucas/exLucas

    Lucas 定理:对于素数 $p$ 有:$C_n^m mod p = C_{lfloor n/p floor}^{lfloor m/p floor} imes C_{n mod p}^{m mod p} mod p$.

    上式的含义即为 $p$ 进制下 $n,m$ 的拆位. 证明可用二项式定理求解,此处略去. 效率 $O(p log_p n)$. 一般 $p$ 为 $10^7$ 及以下级别的素数,如 $10000019$.

     代码如下:

    inline long long Lucas ( int x,int y )
    {
        if ( x<y ) return 0;
        else if ( x<p ) return fct[x]*inv[y]*inv[x-y]%p;
        else return Lucas(x/p,y/p)*Lucas(x%p,y%p)%p;
    }
    Lucas

    exLucas:求 $C_n^m mod p$,其中 $p$ 为任意正整数.

    考虑对 $p$ 进行分解. 转化为每个在模素因数的幂意义下的组合数取余. 分别解出后用 CRT 合并.

    现在考虑 $C_n^m mod p^k$. 将组合数展开为阶乘形式,分别计算.

    对于 $n!$,考虑提取所有 $p$ 的倍数的因子,以 $n=19,p=3$ 为例有:

    $19!=1 imes 2 imes dots imes 19=1 imes 2 imes 4 imes 5 imes dots imes 16 imes 17 imes 19 imes (3 imes 6 imes dots imes 18)$.

    前面一部分整段的直接算,最后一段每个单独计算即可. 后面部分提取出一个 $p$ 后转化为更小的阶乘,递归求解.

    代码如下:

     1 inline long long FCT ( long long n,long long x,long long p )
     2 {
     3     if ( !n ) return 1;
     4     long long s=1;
     5     for ( long long i=1;i<=p;i++ ) if ( i%x ) s=mul(s,i,p);
     6     s=power(s,n/p,p);
     7     for ( long long i=n/p*p+1;i<=n;i++ ) if ( i%x ) s=mul(s,i,p);
     8     return mul(s,FCT(n/x,x,p),p);
     9 }
    10 inline long long INV ( long long a,long long b ) { long long x,y;exgcd(a,b,x,y);return (x%b+b)%b; }
    11 inline long long multiLucas ( long long n,long long m,long long x,long long p )
    12 {
    13     int cnt=0;
    14     for ( long long i=n;i;i/=x ) cnt+=i/x;
    15     for ( long long i=m;i;i/=x ) cnt-=i/x;
    16     for ( long long i=n-m;i;i/=x ) cnt-=i/x;
    17     return mul(mul(power(x,cnt,p),FCT(n,x,p),p),mul(INV(FCT(m,x,p),p),INV(FCT(n-m,x,p),p),p),p);
    18 }
    19 inline long long exLucas ( long long n,long long m,long long p )
    20 {
    21     int cnt=0;long long mod[20]={0},a[20]={0};
    22     for ( long long i=2;i*i<=p;i++ ) if ( !(p%i) )
    23     {
    24         mod[++cnt]=1;
    25         while ( !(p%i) ) mod[cnt]*=i,p/=i;
    26         a[cnt]=multiLucas(n,m,i,mod[cnt]);
    27     }
    28     if ( p>1 ) mod[++cnt]=p,a[cnt]=multiLucas(n,m,p,p);
    29     return CRT(cnt,a,mod);
    30 }
    exLucas


    Day3 20.02.13

    一、常系数一阶线性递推

    递推式:$a_i=ka_{i-1}+b(k eq 0)$.

    通项公式:$a_n=egin{cases}a_0+nb&k=1\a_0k^n+frac{b}{k-1}k^n-frac{b}{k-1}&k eq 1end{cases}$.

    证明:构造 $a_i+x_0=k(a_{i-1}+x_0)$ 后运用等比数列相关知识即可.

    【例题】DTOJ 1378 随机数生成器

    【题目链接】http://59.61.75.5:8018/problem/1378

    【题解】

    直接分类讨论即可. 解方程时用 BSGS 求解.

    【代码】

     1 #include<bits/stdc++.h>
     2 inline int read ( void )
     3 {
     4     int x=0;char ch;
     5     while ( !isdigit(ch=getchar()) ) ;
     6     for ( x=ch^48;isdigit(ch=getchar()); ) x=(x<<1)+(x<<3)+(ch^48);
     7     return x;
     8 }
     9 inline int power ( int x,int y,int mod )
    10 {
    11     int z=1;
    12     for ( ;y;y>>=1,x=1LL*x*x%mod ) if ( y&1 ) z=1LL*z*x%mod;
    13     return z;
    14 }
    15 std::unordered_map<int,int> map;
    16 inline int BSGS ( int y,int z,int p )
    17 {
    18     int m=(int)sqrt(p)+1;map.clear();
    19     if ( z%p==1 ) return 1;
    20     for ( int i=0,t=z;i<m;i++,t=1LL*t*y%p ) map[t]=i;
    21     for ( int i=1,v=power(y,m,p),t=v;i<=m+1;i++,t=1LL*t*v%p ) if ( map.count(t) ) return (i*m-map[t]+1)%p;
    22     return -1;
    23 }
    24 signed main()
    25 {
    26     for ( int T=read();T--; )
    27     {
    28         int p=read(),a=read(),b=read(),X1=read(),t=read(),ans=-1;
    29         if ( X1==t ) ans=1;
    30         else if ( a==1 )
    31         {
    32             if ( b ) ans=(1LL*(t-X1+p)*power(b,p-2,p)+1)%p;
    33         }
    34         else if ( a==0 )
    35         {
    36             if ( b==t ) ans=2;
    37         }
    38         else
    39         {
    40             int res=1LL*b*power((a-1+p)%p,p-2,p)%p;
    41             ans=BSGS(a,1LL*(t+res)%p*power((X1+res)%p,p-2,p)%p,p);
    42         }
    43         if ( !ans ) ans+=p;
    44         printf("%d
    ",ans);
    45     }
    46     return 0;
    47 }
    DTOJ1378

    二、常系数二阶齐次线性递推

    递推式:$a_i=pa_{i-1}+qa_{i-2}(p,q eq 0)$.

    通项公式:设 $alpha,eta$ 为方程 $x^2-px-q=0$ 两根,则 $a_n=Aalpha^n+Beta^n$. 其中 $A,B$ 为常数,可用待定系数法求解.

    证明:构造 $a_n - alpha a_{n-1}= eta(a_{n-1}-alpha a_{n-2})$ 即可.

    注意到 $alpha,eta$ 常为无理数,可用二次剩余或矩阵维护.

    【例题】DTOJ 4558「JLOI2015」有意义的字符串

    【题目链接】http://59.61.75.5:8018/problem/4558

    【题解】

    考虑共轭. 构造方程 $x^2+frac{d-b^2}{4}x+b=0$. 设两根为 $p,q$. 则题目要求为 $p^n$.

    考虑 $f_n=p^n+q^n=(p+q)(p^{n-1}+q^{n-1})-pq(p^{n-2}+q^{n-2})=(p+q)f_{n-1}-pqf_{n-2}$.

    直接计算即可. 又 $q^n$ 较小,对其讨论即可.

    【代码】

     1 #include<bits/stdc++.h>
     2 const unsigned long long mod=7528443412579576937llu;
     3 unsigned long long b,d,n,ans;
     4 inline unsigned long long mul ( unsigned long long a,unsigned long long b )
     5 {
     6     unsigned long long res=0;
     7     while ( b )
     8     {
     9         if ( b&1 ) res=(res+a)%mod;
    10         a=(a+a)%mod;b>>=1;
    11     }
    12     return res;
    13 }
    14 struct matrix
    15 {
    16     unsigned long long a[2][2];
    17     inline friend matrix operator * ( const matrix &A,const matrix &B )
    18     {
    19         matrix C;memset(C.a,0,sizeof(C.a));
    20         for ( int i=0;i<2;i++ ) for ( int k=0;k<2;k++ ) for ( int j=0;j<2;j++ ) C.a[i][j]=(C.a[i][j]+mul(A.a[i][k],B.a[k][j]))%mod;
    21         return C;
    22     }
    23     inline friend matrix operator ^ ( matrix A,unsigned long long n )
    24     {
    25         matrix B;B.a[0][0]=B.a[1][1]=1;B.a[0][1]=B.a[1][0]=0;
    26         while ( n )
    27         {
    28             if ( n&1 ) B=B*A;
    29             A=A*A;n>>=1;
    30         }
    31         return B;
    32     }
    33 }A;
    34 signed main()
    35 {
    36     scanf("%llu%llu%llu",&b,&d,&n);
    37     if ( n==0 ) ans=2;
    38     else if ( n==1 ) ans=b;
    39     else A.a[0][0]=b,A.a[0][1]=mul((d-b*b%mod+mod)%mod,mul((mod+1)>>1,(mod+1)>>1)),A.a[1][0]=1,A=A^(n-1),ans=(mul(A.a[0][0],b)+mul(A.a[0][1],2))%mod;
    40     if ( b*b!=d and !(n&1) ) ans=(ans-1+mod)%mod;
    41     return !printf("%llu
    ",ans);
    42 }
    DTOJ4558

    三、Fibonacci 循环节

    显然 Fibonacci 数列在 $mod p$ 意义下有循环节,且循环节长度不大于 $p^2$.

    由于证明较为复杂,此处仅给出结论:

    设 $G(p)$ 为 $mod p$ 意义下循环节. 对 $p$ 分解有: $p=prodlimits_{i=1}^{k} p_i^{a_i}$.

    则 $G(p)=prodlimits_{i=1}^{k} G(p_i^{a_i})=prodlimits_{i=1}^{k} G(p_i) imes p_i^{a_i-1}$.

    对于 $G(p_i)$ 又有:若 $p_i^2 equiv 1 pmod 5$,则 $G(p_i) mid p-1$. 否则 $G(p_i) mid 2(p+1)$.

    又实际上无需确切算出最小循环节,用 $p-1$ 及 $2(p+1)$ 代替即可.

    【例题】DTOJ 4688 迫害 DJ

    【题目链接】http://59.61.75.5:8018/problem/4688

    【题解】

    迭代 $k$ 次,每次求出循环节即可.

    【代码】

     1 #include<bits/stdc++.h>
     2 inline long long read ( void )
     3 {
     4     long long x=0;char ch;
     5     while ( !isdigit(ch=getchar()) ) ;
     6     for ( x=ch^48;isdigit(ch=getchar()); ) x=(x<<1)+(x<<3)+(ch^48);
     7     return x;
     8 }
     9 long long a,b,pr[1000000],cnt[1000000],tot;
    10 std::unordered_map<long long,long long> map;
    11 inline long long mul ( long long x,long long y,long long p ) { return x*y-(long long)((long double)x*y/p)*p; }
    12 std::pair<long long,long long> calc ( long long n,long long p )
    13 {
    14     if ( n==-1 ) return std::make_pair(1,0);
    15     if ( n==0 ) return std::make_pair(0,1);
    16     if ( n%2==1 )
    17     {
    18         std::pair<long long,long long> res=calc(n-1,p);
    19         return std::make_pair(res.second,(res.first+res.second)%p);
    20     }
    21     else
    22     {
    23         std::pair<long long,long long> res=calc(n/2,p);
    24         return std::make_pair(mul((2*res.second-res.first+p)%p,res.first,p),(mul(res.first,res.first,p)+mul(res.second,res.second,p))%p);
    25     }
    26 }
    27 inline long long get ( long long n,long long p )
    28 {
    29     std::pair<long long,long long> res=calc(2*n-1,p);
    30     return (mul(a,res.first,p)+mul((b-a),res.second,p))%p;
    31 }
    32 inline long long G ( long long p )
    33 {
    34     if ( p==2 ) return 3;
    35     if ( p==3 ) return 8;
    36     if ( p==5 ) return 20;
    37     return ( p%5==4 or p%5==1 ) ? p-1 : 2*(p+1) ;
    38 }
    39 inline long long find_loop ( long long n )
    40 {
    41     tot=0;
    42     for ( long long i=2;i*i<=n;i++ ) if ( !(n%i) )
    43     {
    44         pr[++tot]=i;cnt[tot]=0;
    45         while ( !(n%i) ) cnt[tot]++,n/=i;
    46     }
    47     if ( n>1 ) pr[++tot]=n,cnt[tot]=1;
    48     long long ans=1;
    49     for ( int i=1;i<=tot;i++ )
    50     {
    51         long long res=G(pr[i]);
    52         for ( int j=2;j<=cnt[i];j++ ) res*=pr[i];
    53         ans=ans/std::__gcd(ans,res)*res;
    54     }
    55     return ans;
    56 }
    57 long long n,k,Mod[150];
    58 signed main()
    59 {
    60     for ( int T=read();T--; )
    61     {
    62         a=read();b=read();n=read();k=read();Mod[1]=read();
    63         for ( int i=2;i<=k;i++ ) Mod[i]=find_loop(Mod[i-1]);
    64         n%=find_loop(Mod[k]);
    65         for ( int i=k;i;i-- ) n=get(n,Mod[i]);
    66         printf("%lld
    ",n);
    67     }
    68     return 0;
    69 }
    DTOJ4688

    【例题】DTOJ 3098 斐波那契数

    【题目链接】http://59.61.75.5:8018/problem/3098

    【题解】

    预处理两个循环节,每次迭代即可.

    【代码】

     1 #include<bits/stdc++.h>
     2 inline long long read ( void )
     3 {
     4     long long x=0;char ch;
     5     while ( !isdigit(ch=getchar()) ) ;
     6     for ( x=ch^48;isdigit(ch=getchar()); ) x=(x<<1)+(x<<3)+(ch^48);
     7     return x;
     8 }
     9 inline long long read ( long long mod )
    10 {
    11     long long x=0;char ch;
    12     while ( !isdigit(ch=getchar()) ) ;
    13     for ( x=ch^48;isdigit(ch=getchar()); ) x=((x<<1)+(x<<3)+(ch^48))%mod;
    14     return x;
    15 }
    16 long long a,b,pr[1000000],cnt[1000000],tot;
    17 std::unordered_map<long long,long long> map;
    18 inline long long mul ( long long x,long long y,long long p )
    19 {
    20     long long z=0;
    21     for ( ;y;y>>=1,x=(x+x)%p ) if ( y&1 ) z=(z+x)%p;
    22     return z;
    23 }
    24 std::pair<long long,long long> calc ( long long n,long long p )
    25 {
    26     if ( n==-1 ) return std::make_pair(1,0);
    27     if ( n==0 ) return std::make_pair(0,1);
    28     if ( n%2==1 )
    29     {
    30         std::pair<long long,long long> res=calc(n-1,p);
    31         return std::make_pair(res.second,(res.first+res.second)%p);
    32     }
    33     else
    34     {
    35         std::pair<long long,long long> res=calc(n/2,p);
    36         return std::make_pair(mul((2*res.second-res.first+p)%p,res.first,p),(mul(res.first,res.first,p)+mul(res.second,res.second,p))%p);
    37     }
    38 }
    39 inline long long get ( long long n,long long p )
    40 {
    41     std::pair<long long,long long> res=calc(n,p);
    42     return res.first;
    43 }
    44 inline long long G ( long long p )
    45 {
    46     if ( p==2 ) return 3;
    47     if ( p==3 ) return 8;
    48     if ( p==5 ) return 20;
    49     return ( p%5==4 or p%5==1 ) ? p-1 : 2*(p+1) ;
    50 }
    51 inline long long find_loop ( long long n )
    52 {
    53     tot=0;
    54     for ( long long i=2;i*i<=n;i++ ) if ( !(n%i) )
    55     {
    56         pr[++tot]=i;cnt[tot]=0;
    57         while ( !(n%i) ) cnt[tot]++,n/=i;
    58     }
    59     if ( n>1 ) pr[++tot]=n,cnt[tot]=1;
    60     long long ans=1;
    61     for ( int i=1;i<=tot;i++ )
    62     {
    63         long long res=G(pr[i]);
    64         for ( int j=2;j<=cnt[i];j++ ) res*=pr[i];
    65         ans=ans/std::__gcd(ans,res)*res;
    66     }
    67     return ans;
    68 }
    69 long long n,Mod[150];
    70 signed main()
    71 {
    72     Mod[1]=1000000007;Mod[2]=find_loop(Mod[1]);Mod[3]=find_loop(Mod[2]);
    73     for ( int T=read();T--; ) printf("%lld
    ",get(get(read(Mod[3]),Mod[2]),Mod[1]));
    74     return 0;
    75 }
    DTOJ3098


    Day4 20.02.14

    一、狄利克雷卷积

    定义:数论函数 $f,g$ 的狄利克雷卷积为:$f*g(n)=sumlimits_{d mid n} f(d) imes g(frac{n}{d})$.

    显然狄利克雷卷积具有非常多的性质,如交换律、结合律、分配律等.

    定义:元函数 $epsilon(n)=[n=1]$. 则有:$epsilon*f=f$.

    定义如下函数:$mathbf{id}(n)=n,mathbf{1}(n)=1$.

    定义逆元:若 $f*g=epsilon$,则称 $g$ 为 $f$ 的逆元.

    定义积性函数:若数论函数 $f$ 满足 $f(nm)=f(n)f(m)(nperp m)$,则 $f$ 为积性函数. 若 $f(nm)=f(n)f(m)$,则 $f$ 为完全积性函数.

    显然根据定义可以得到,积性函数的狄利克雷卷积也是积性函数.

    根据积性函数的特性,可以与线性筛结合预处理. 在筛的时候顺路处理函数值即可.

    显然欧拉函数 $varphi$ 及上述提到的数论函数均为积性函数.

    结论:$mathbf{id}=varphi * mathbf{1}$. 证明根据定义.

    二、莫比乌斯反演

    定义 $mathbf{1}$ 的逆元为 $mu$. 则有:$epsilon=mu * mathbf{1}$.

    则有:若 $g=f*mathbf{1}$,则 $g*mu = f*mathbf{1}*mu$,即 $f=g*mu$.

    莫比乌斯反演定理:若 $g(n)=sumlimits_{dmid n} f(d)$,则 $f(n)=sumlimits_{dmid n} g(d)mu(frac{n}{d})$.

    关于 $mu$ 有:$mu(n)=egin{cases}1&n=1\(-1)^k&n=prodlimits_{i=1}^k p_i\0&otherwiseend{cases}$.

    另一方向,即枚举的 $d$ 改为 $n$ 的倍数,也有类似结论的反演定理.

    具体为:若 $g(n)=sumlimits_{nmid d} f(d)$,则 $f(n)=sumlimits_{nmid d} g(d)mu(frac{d}{n})$.

    这个式子常常作为容斥的思路出现,往往答案需要求的为 $f(1)$,而 $f$ 本身不容易求出,$g$ 容易求出,则有 $f(1)=sumlimits_{i=1}^{n}g(i)mu(i)$.

    【例题】DTOJ 4698 亚特兰大

    【题目链接】http://59.61.75.5:8018/problem/4690

    【题解】

    显然 $gcd$ 为 $x$ 的路径条数不好求,但 $gcd$ 为 $x$ 的倍数的可以求出.

    考虑反演,用第二个公式可得:$f(1)=sumlimits_{i=1}^{n}g(i)mu(i)$.

    现在考虑如何求 $g$. 显然对于 $g(d)$,将所有边权为 $d$ 的边提出处理即可. 具体处理方式可以用可持久化并查集实现.

    【代码】

     1 #include<bits/stdc++.h>
     2 inline int read ( void )
     3 {
     4     int x=0;char ch;
     5     while ( !isdigit(ch=getchar()) ) ;
     6     for ( x=ch^48;isdigit(ch=getchar()); ) x=(x<<1)+(x<<3)+(ch^48);
     7     return x;
     8 }
     9 const int maxn=100000+10;
    10 const int maxm=1000000+10;
    11 int n,m=1000000,Q,mu[maxm];bool flag[maxm],vis[200];
    12 struct edge { int u,v,w,t; } E[maxn];
    13 std::vector<edge> G[maxm];
    14 std::pair<int,int> q[200];
    15 std::vector<int> pr,B;
    16 int f[maxn],siz[maxn],W[200][200],map[maxn],tot;
    17 long long Ans[200],res;
    18 inline void R ( int x ) { f[x]=x;siz[x]=1; }
    19 inline int find ( int x ) { return f[x]==x ? x : find(f[x]); }
    20 std::stack<std::pair<int,int>> st;
    21 std::set<int> A;
    22 inline void merge ( int u,int v )
    23 {
    24     u=find(u);v=find(v);
    25     res-=1LL*siz[u]*(siz[u]-1)/2;
    26     res-=1LL*siz[v]*(siz[v]-1)/2;
    27     siz[u]+=siz[v];f[v]=u;
    28     res+=1LL*siz[u]*(siz[u]-1)/2;
    29     st.push(std::make_pair(u,v));
    30 }
    31 inline void split ( void )
    32 {
    33     auto p=st.top();st.pop();
    34     int u=p.first,v=p.second;
    35     res-=1LL*siz[u]*(siz[u]-1)/2;
    36     siz[u]-=siz[v];f[v]=v;
    37     res+=1LL*siz[v]*(siz[v]-1)/2;
    38     res+=1LL*siz[u]*(siz[u]-1)/2;
    39 }
    40 signed main()
    41 {
    42     mu[1]=1;
    43     for ( int i=2;i<=m;i++ )
    44     {
    45         if ( !flag[i] ) pr.push_back(i),mu[i]=-1;
    46         for ( int j:pr )
    47         {
    48             if ( i*j>m ) break;
    49             flag[i*j]=true;mu[i*j]=-mu[i];
    50             if ( !(i%j) ) { mu[i*j]=0;break; }
    51         }
    52     }
    53     n=read();
    54     for ( int i=1;i<n;i++ ) E[i].u=read(),E[i].v=read(),E[i].w=read(),E[i].t=-1;
    55     Q=read();
    56     for ( int i=1,k,x;i<=Q;i++ ) k=read(),x=read(),E[k].t=0,q[i]=std::make_pair(k,x),A.insert(k);
    57     for ( int k:A ) B.push_back(k),map[k]=tot++;
    58     for ( int k=0;k<tot;k++ ) for ( int i=0;i<=Q;i++ ) W[k][i]=E[B[k]].w;
    59     for ( int i=1;i<=Q;i++ ) for ( int j=i;j<=Q;j++ ) W[map[q[i].first]][j]=q[i].second;
    60     for ( int i=1;i<n;i++ ) if ( E[i].t==-1 ) for ( int j=1;j*j<=E[i].w;j++ ) if ( !(E[i].w%j) )
    61     {
    62         if ( mu[j] ) G[j].push_back(E[i]);
    63         if ( j*j!=E[i].w and mu[E[i].w/j] ) G[E[i].w/j].push_back(E[i]);
    64     }
    65     for ( int j=0;j<=Q;j++ ) for ( int i=0;i<tot;i++ )
    66     {
    67         int u=E[B[i]].u,v=E[B[i]].v;
    68         for ( int k=1;k*k<=W[i][j];k++ ) if ( !(W[i][j]%k) )
    69         {
    70             if ( mu[k] ) G[k].push_back((edge){u,v,W[i][j],j});
    71             if ( k*k!=W[i][j] and mu[W[i][j]/k] ) G[W[i][j]/k].push_back((edge){u,v,W[i][j],j});
    72         }
    73     }
    74     for ( int x=1;x<=m;x++ ) if ( mu[x] )
    75     {
    76         res=0;
    77         for ( edge e:G[x] ) R(e.u),R(e.v);
    78         for ( int i=0,j;i<(int)G[x].size();i=j )
    79         {
    80             for ( j=i;j<(int)G[x].size() and G[x][j].t==G[x][i].t;j++ ) merge(G[x][j].u,G[x][j].v);
    81             if ( ~G[x][i].t )
    82             {
    83                 Ans[G[x][i].t]+=mu[x]*res;
    84                 for ( int k=j-1;k>=i;k-- ) split();
    85             }
    86             else
    87             {
    88                 for ( int k=0;k<=Q;k++ ) vis[k]=false;
    89                 for ( int k=j;k<(int)G[x].size();k++ ) vis[G[x][k].t]=true;
    90                 for ( int k=0;k<=Q;k++ ) if ( !vis[k] ) Ans[k]+=mu[x]*res;
    91             }
    92         }
    93     }
    94     for ( int i=0;i<=Q;i++ ) printf("%lld
    ",Ans[i]);
    95     return 0;
    96 }
    DTOJ4690

    【例题】求 $sumlimits_{i=1}^n sumlimits_{j=1}^m gcd(i,j)$.

    【题解一】

    设 $N=min(m,n)$,考虑每个数的贡献:

    $$egin{aligned} sumlimits_{i=1}^n sumlimits_{j=1}^m gcd(i,j) &=sumlimits_{i=1}^N sumlimits_{j=1}^N gcd(i,j)
    \ &= sumlimits_{d=1}^N sumlimits_{i=1}^{lfloor frac{n}{d} floor} sumlimits_{j=1}^{lfloor frac{m}{d} floor} [gcd(i,j)==1]
    \ &=sumlimits_{d=1}^N d sumlimits_{i=1}^{lfloor frac{N}{d} floor} mu(i) lfloor frac{n}{id} floor lfloor frac{m}{id} floor
    \&=  sumlimits_{d=1}^N  d sumlimits_{d mid T} mu(frac{T}{d}) lfloor frac{n}{T} floor lfloor frac{m}{T} floor
    \&=sumlimits_{T=1}^{N} sumlimits_{d mid T} dmu(frac{T}{d}) lfloor frac{n}{T} floor lfloor frac{m}{T} floor
    \&=sumlimits_{T=1}^{N} varphi(T)  lfloor frac{n}{T} floor lfloor frac{m}{T} floor end{aligned}$$

    这里应用到了一个重要结论:$varphi=mathbf{id}*mu$,证明由狄利克雷卷积部分最后给出的一个式子得到.

    【题解二】

    $$egin{aligned} sumlimits_{i=1}^n sumlimits_{j=1}^m gcd(i,j) &= sumlimits_{i=1}^n sumlimits_{j=1}^m sumlimits_{dmid i, dmid j} varphi(d)
    \&=sumlimits_{d=1}^{min(n,m)} varphi(d) lfloor frac{n}{d} floor lfloor frac{m}{d} floor end{aligned}$$



    Day5 20.02.15

    一、整除分块(数论分块)

    在数论的题目中,常常出现 $sumlimits_{i=1}^{n} f(i) lfloor frac{n}{i} floor$ 形式的式子(如 Day4 莫比乌斯反演例题)。

    考虑到 $lfloor frac{n}{i} floor$ 的取值只有 $sqrt n$ 种(易证),若 $f$ 的前缀和能够求出,则可以 $O(sqrt n)$ 计算上式.

    代码如下:

    1 inline long long solve ( int n )
    2 {
    3     long long res=0;
    4     for ( int i=1,j;i<=n;i=j+1 ) j=n/(n/i),res+=(calc(j)-calc(i-1))*(n/i);
    5     return res;
    6 }
    整除分块(数论分块)

    二、杜教筛

    令 $S(n)=sumlimits_{i=1}^{n} f(i)$.

    则有:$sumlimits_{i=1}^{n} f*g(i)=sumlimits_{i=1}^{n} sumlimits_{xy=i}f(x)g(y)=sumlimits_{y=1}^{n}g(y)sumlimits_{xy leq n} f(x)=sumlimits_{y=1}^{n}g(y)S(lfloor frac{n}{y} floor)$.

    因此可得 $g(1)S(n)=sumlimits_{i=1}^{n} f*g(i)-sumlimits_{y=2}^{n}g(y)S(lfloor frac{n}{y} floor)$.

    因此若 $f*g$ 及 $g$ 的前缀和容易求出,则可以预处理 $n^{frac{2}{3}}$ 的部分,剩余的 $O(sqrt n)$ 计算(数论分块+记忆化搜索),效率 $O(n^{frac{2}{3}})$(为什么是这个效率我也不知道).

    【例题】luogu P4213 【模板】杜教筛(Sum)

    【题解】$mathbf{id}=varphi * mathbf{1},epsilon=mu * mathbf{1}$. 杜教筛.

    【代码】

     1 #include<bits/stdc++.h>
     2 #define print(n) std::cerr<<#n<<'='<<n<<'
    '
     3 inline int read ( void )
     4 {
     5     int n=0;char ch;bool flag=true;
     6     while ( !isdigit(ch=getchar()) ) if ( ch=='-' ) flag=false;
     7     for ( n=ch^48;isdigit(ch=getchar()); ) n=(n<<1)+(n<<3)+(ch^48);
     8     return flag ? n : -n ;
     9 }
    10 const int m=1700000;
    11 int pr[m+10],tot;
    12 long long mu[m+10],phi[m+10];
    13 bool flag[m+10];
    14 std::unordered_map<int,long long> summu,sumphi;
    15 inline int sum_mu ( int n )
    16 {
    17     if ( n<=m ) return mu[n];
    18     if ( summu.count(n) ) return summu[n];
    19     long long ans=1;
    20     for ( register int i=2,j;i<=n;i=j+1 ) j=n/(n/i),ans-=(j-i+1)*sum_mu(n/i);
    21     return summu[n]=ans;
    22 }
    23 inline long long sum_phi ( int n )
    24 {
    25     if ( n<=m ) return phi[n];
    26     if ( sumphi.count(n) ) return sumphi[n];
    27     long long ans=1LL*n*(n+1)/2;
    28     for ( register int i=2,j;i<=n;i=j+1 ) j=n/(n/i),ans-=(j-i+1)*sum_phi(n/i);
    29     return sumphi[n]=ans;
    30 }
    31 int main()
    32 {
    33     mu[1]=phi[1]=1;
    34     for ( register int i=2;i<=m;i++ )
    35     {
    36         if ( !flag[i] ) pr[++tot]=i,phi[i]=i-1,mu[i]=-1;
    37         for ( register int j=1;j<=tot && i*pr[j]<=m;j++ )
    38         {
    39             flag[i*pr[j]]=true;
    40             if ( !(i%pr[j]) ) { mu[i*pr[j]]=0;phi[i*pr[j]]=phi[i]*pr[j];break; }
    41             mu[i*pr[j]]=-mu[i];phi[i*pr[j]]=phi[i]*(pr[j]-1);
    42         }
    43     }
    44     for ( register int i=2;i<=m;i++ ) mu[i]+=mu[i-1],phi[i]+=phi[i-1];
    45     for ( int T=read(),n;T;T-- ) n=read(),printf("%lld %d
    ",sum_phi(n),sum_mu(n));
    46     return 0;
    47 }
    luogu P4213 


    Day6 20.02.16

    【例题】http://59.61.75.5:8018/problem/4704

    【题解】https://www.cnblogs.com/RenSheYu/p/12266604.html

    【例题】https://www.luogu.com.cn/problem/P3768

    【题解】

    $$egin{aligned}sumlimits_{i=1}^n sumlimits_{j=1}^n ijgcd(i,j) &=  sumlimits_{i=1}^n sumlimits_{j=1}^n ij sumlimits_{d mid i,d mid j} varphi(d)
    \&=sumlimits_{d=1}^n d^2varphi(d) (sumlimits_{i=1}^{lfloor frac{n}{d} floor} i) (sumlimits_{j=1}^{lfloor frac{n}{d} floor} j)
    \&=sumlimits_{d=1}^n d^2varphi(d) (S(lfloor frac{n}{d} floor))^2 end{aligned}$$

    其中 $S(n)=sumlimits_{i=1}^{n} i=frac{n(n+1)}{2}$. 设 $f(n)=n^2varphi(n),g(n)=n^2,h(n)=sumlimits_{d=1}^n d^2varphi(d)$. 则有:

    $$h(n)=sumlimits_{i=1}^{n} f*g(i)-sumlimits_{y=2}^{n}g(y)S(lfloor frac{n}{y} floor)$$

    $$f*g(i)=sumlimits_{dmid i} f(d)g(lfloor frac{n}{d} floor)=i^2 sumlimits_{dmid i} varphi(d)=i^3$$

    $$h(n)=sumlimits_{i=1}^{n} i^3-sumlimits_{y=2}^{n}y^2S(lfloor frac{n}{y} floor)$$

    杜教筛.

    【代码】

     1 #include<bits/stdc++.h>
     2 const int maxm=10000000+10;
     3 int m=10000000,p,val[maxm],inv2,inv6;
     4 long long n,phi[maxm];
     5 bool flag[maxm];
     6 std::vector<int> pr;
     7 std::map<long long,int> map;
     8 inline int power ( int x,int y )
     9 {
    10     int z=1;
    11     for ( ;y;y>>=1,x=1LL*x*x%p ) if ( y&1 ) z=1LL*z*x%p;
    12     return z;
    13 }
    14 inline int S1 ( long long n ) { return n%p*((n+1)%p)%p*inv2%p; }
    15 inline int S2 ( long long n ) { return n%p*((n+1)%p)%p*((2*n+1)%p)%p*inv6%p; }
    16 inline int S3 ( long long n ) { return 1LL*S1(n)*S1(n)%p; }
    17 inline int h ( long long n )
    18 {
    19     if ( n<=m ) return val[n];
    20     if ( map.count(n) ) return map[n];
    21     int res=S3(n);
    22     for ( long long i=2,j;i<=n;i=j+1 ) j=n/(n/i),res=(res-1LL*h(n/i)*(S2(j)-S2(i-1)+p)%p+p)%p;
    23     return map[n]=res;
    24 }
    25 signed main()
    26 {
    27     scanf("%d%lld",&p,&n);int ans=0;
    28     inv2=power(2,p-2);inv6=power(6,p-2);
    29     phi[1]=1;
    30     for ( int i=2;i<=m;i++ )
    31     {
    32         if ( !flag[i] ) pr.push_back(i),phi[i]=i-1;
    33         for ( int j:pr )
    34         {
    35             if ( i*j>m ) break;
    36             flag[i*j]=true;phi[i*j]=phi[i]*(j-1);
    37             if ( !(i%j) ) { phi[i*j]=phi[i]*j;break; }
    38         }
    39     }
    40     for ( int i=1;i<=m;i++ ) val[i]=(val[i-1]+1LL*i*i%p*phi[i])%p;
    41     for ( long long i=1,j;i<=n;i=j+1 ) j=n/(n/i),ans=(ans+1LL*(h(j)-h(i-1)+p)*S1(n/i)%p*S1(n/i))%p;
    42     return !printf("%d
    ",ans);
    43 }
    luogu P3768

    【例题】http://59.61.75.5:8018/problem/1551

    【题解】

    显然 $O(abc)$ 枚举. 考虑线性求 $mathbf{d}(n)$.

    考虑约数个数定理,有约数个数为素因数幂的指数加一之积.

    记 $num(i)$ 为 $i$ 包含的最小素因数的幂的指数. 线性筛处理即可. 细节参见代码.

    【代码】

     1 #include<bits/stdc++.h>
     2 const unsigned int mod=1073741824;
     3 const int maxn=8000000+10;
     4 int a,b,c,n,num[maxn];
     5 unsigned d[maxn],ans;
     6 bool flag[maxn];
     7 std::vector<int> pr;
     8 signed main()
     9 {
    10     scanf("%d%d%d",&a,&b,&c);n=a*b*c;
    11     d[1]=1;
    12     for ( int i=2;i<=n;i++ )
    13     {
    14         if ( !flag[i] ) pr.push_back(i),num[i]=1,d[i]=2;
    15         for ( int j:pr )
    16         {
    17             if ( i*j>n ) break;
    18             flag[i*j]=true;num[i*j]=1;d[i*j]=d[i]*d[j];
    19             if ( !(i%j) ) { num[i*j]=num[i]+1;d[i*j]=d[i]/(num[i]+1)*(num[i*j]+1);break; }
    20         }
    21     }
    22     for ( int i=1;i<=a;i++ ) for ( int j=1;j<=b;j++ ) for ( int k=1;k<=c;k++ ) ans=(ans+d[i*j*k])%mod;
    23     return !printf("%u
    ",ans);
    24 }
    DTOJ1551

    【例题】http://59.61.75.5:8018/problem/2304

    【题解】

    设 $f(n)=sumlimits_{i=1}^n lfloor frac{n}{i} floor=sumlimits_{i=1}^n mathbf{d}(i)$.

    $$egin{aligned}sumlimits_{i=1}^{n}sumlimits_{j=1}^{m} mathbf{d}(ij)
    &= sumlimits_{i=1}^{n}sumlimits_{j=1}^{m} sumlimits_{amid i} sumlimits_{b mid j} [gcd(a,b)==1]
    \&= sumlimits_{a=1}^n sumlimits_{b=1}^m [gcd(a,b)==1] lfloor frac{n}{a}  floor lfloor frac{m}{b}  floor
    \&= sumlimits_{d=1}^{min(n,m)} mu(d) (sumlimits_{a=1}^{lfloor frac{n}{d} floor} lfloor frac{n}{ad} floor)(sumlimits_{b=1}^{lfloor frac{m}{d} floor} lfloor frac{m}{bd} floor)
    \&= sumlimits_{d=1}^{min(n,m)} mu(d)f(lfloor frac{n}{d} floor)f(lfloor frac{m}{d} floor)end{aligned}$$

    约数个数可以线性筛处理(同上题),数论分块即可.

    【代码】

     1 #include<bits/stdc++.h>
     2 inline int read ( void )
     3 {
     4     int x=0;char ch;
     5     while ( !isdigit(ch=getchar()) ) ;
     6     for ( x=ch^48;isdigit(ch=getchar()); ) x=(x<<1)+(x<<3)+(ch^48);
     7     return x;
     8 }
     9 const int maxn=50000+10;
    10 int a,b,c,mu[maxn],num[maxn];
    11 long long d[maxn];bool flag[maxn];
    12 std::vector<int> pr;
    13 signed main()
    14 {
    15     mu[1]=d[1]=1;
    16     for ( int i=2;i<=50000;i++ )
    17     {
    18         if ( !flag[i] ) pr.push_back(i),num[i]=1,d[i]=2,mu[i]=-1;
    19         for ( int j:pr )
    20         {
    21             if ( i*j>50000 ) break;
    22             flag[i*j]=true;num[i*j]=1;d[i*j]=d[i]*d[j];mu[i*j]=-mu[i];
    23             if ( !(i%j) ) { num[i*j]=num[i]+1;d[i*j]=d[i]/(num[i]+1)*(num[i*j]+1);mu[i*j]=0;break; }
    24         }
    25     }
    26     for ( int i=2;i<=50000;i++ ) mu[i]+=mu[i-1],d[i]+=d[i-1];
    27     for ( int T=read();T--; )
    28     {
    29         int n=read(),m=read();long long ans=0;
    30         for ( int i=1,j;i<=std::min(n,m);i=j+1 )
    31         {
    32             j=std::min(n/(n/i),m/(m/i));
    33             ans+=1LL*d[n/i]*d[m/i]*(mu[j]-mu[i-1]);
    34         }
    35         printf("%lld
    ",ans);
    36     }
    37     return 0;
    38 }
    DTOJ2304


    Day7 20.02.17

    【例题】https://www.luogu.com.cn/problem/P1829

    【题解】

    设 $n < m, S(n) = sumlimits_{i=1}^{n} i =frac{n(n+1)}{2}$.

    $$egin{aligned} sumlimits_{i=1}^n sumlimits_{j=1}^m operatorname{lcm}(i,j)
    &=sumlimits_{i=1}^n sumlimits_{j=1}^m frac{ij}{gcd(i,j)}
    \&=sumlimits_{i=1}^n sumlimits_{j=1}^m sumlimits_{dmid i,dmid j} frac{ij}{d} [gcd(i,j)==d]
    \&=sumlimits_{d=1}^n dsumlimits_{i=1}^{lfloor frac{n}{d} floor} sumlimits_{j=1}^{lfloor frac{m}{d} floor} ij sumlimits_{emid i,emid j} mu(e)
    \&=sumlimits_{d=1}^n dsumlimits_{e=1}^{lfloor frac{n}{d} floor} e^2mu(e) sumlimits_{i=1}^{lfloor frac{n}{de} floor} sumlimits_{j=1}^{lfloor frac{m}{de} floor} ij
    \&=sumlimits_{d=1}^n dsumlimits_{e=1}^{lfloor frac{n}{d} floor} e^2mu(e) S(lfloor frac{n}{de} floor) S(lfloor frac{m}{de} floor)
    \&=sumlimits_{d=1}^n dsumlimits_{e=1}^{lfloor frac{n}{d} floor} e^2mu(e) S(lfloor frac{lfloor frac{n}{d} floor}{e} floor) S(lfloor frac{lfloor frac{m}{d} floor}{e} floor)
    end{aligned}$$

    设 $f(n,m)=sumlimits_{i=1}^n i^2 mu(i) S(lfloor frac{n}{i} floor)S(lfloor frac{m}{i} floor)$.

    $$sumlimits_{d=1}^n dsumlimits_{e=1}^{lfloor frac{n}{d} floor} e^2mu(e) S(lfloor frac{lfloor frac{n}{d} floor}{e} floor) S(lfloor frac{lfloor frac{m}{d} floor}{e} floor)=sumlimits_{d=1}^n df(lfloor frac{n}{d} floor,lfloor frac{m}{d} floor)$$.

    显然外层一个数论分块,内层求 $f$ 再用一个数论分块,效率 $O(n+m)$. 即可通过本题.

    【代码】

     1 #include<bits/stdc++.h>
     2 const int mod=20101009,inv2=10050505;
     3 const int maxn=10000000+10;
     4 int ans,n,m,mu[maxn],sum[maxn];
     5 std::vector<int> pr;
     6 bool flag[maxn];
     7 inline int S1 ( int n ) { return 1LL*n*(n+1)%mod*inv2%mod; }
     8 inline int calc ( int n,int m )
     9 {
    10     int res=0;
    11     for ( int i=1,j;i<=n;i=j+1 ) j=std::min(n/(n/i),m/(m/i)),res=(res+1LL*(sum[j]-sum[i-1]+mod)*S1(n/i)%mod*S1(m/i))%mod;
    12     return res;
    13 }
    14 signed main()
    15 {
    16     scanf("%d%d",&n,&m);
    17     if ( n>m ) std::swap(n,m);
    18     mu[1]=1;
    19     for ( int i=2;i<=n;i++ )
    20     {
    21         if ( !flag[i] ) pr.push_back(i),mu[i]=-1;
    22         for ( int j:pr )
    23         {
    24             if ( i*j>n ) break;
    25             flag[i*j]=true;mu[i*j]=-mu[i];
    26             if ( !(i%j) ) { mu[i*j]=0;break; }
    27         }
    28     }
    29     for ( int i=1;i<=n;i++ ) sum[i]=(sum[i-1]+1LL*i*i%mod*mu[i]%mod+mod)%mod;
    30     for ( int i=1,j;i<=n;i=j+1 ) j=std::min(n/(n/i),m/(m/i)),ans=(ans+1LL*calc(n/i,m/i)*(S1(j)-S1(i-1)+mod))%mod;
    31     return !printf("%d
    ",ans);
    32 }
    luogu P1829

    【例题】https://www.luogu.com.cn/problem/P2257

    【题解】

    从本篇题解起,往下均有:$n<m,mathbf{d}(n)=sumlimits_{dmid n}1,S_k(n)=sumlimits_{i=1}^n i^k$. 其中 $k=1$ 有时省略不写.

    显然考虑枚举素数 $p$,考虑每个素数的贡献. 设 $pr$ 为素数集合.

    $$egin{aligned} sumlimits_{pin pr} sumlimits_{i=1}^n sumlimits_{j=1}^n [gcd(i,j)==p]
    &=sumlimits_{pin pr} sumlimits_{i=1}^{lfloor frac{n}{p} floor} sumlimits_{i=1}^{lfloor frac{m}{p} floor} [gcd(i,j)==1]
    \&=sumlimits_{pin pr} sumlimits_{i=1}^{lfloor frac{n}{p} floor} sumlimits_{i=1}^{lfloor frac{m}{p} floor} sumlimits_{dmid i,dmid j} mu(d)
    \&=sumlimits_{pint pr} sumlimits_{d=1}^{lfloor frac{n}{p} floor} mu(d) lfloor frac{n}{pd} floor lfloor frac{m}{pd} floor
    end{aligned}$$

    一般遇到这类整除 $pd$ 型的式子,通常会设 $T=pd$,然后转化为枚举 $T$. 此处采用同样的方式:

    $$egin{aligned} sumlimits_{pint pr} sumlimits_{d=1}^{lfloor frac{n}{p} floor} mu(d) lfloor frac{n}{pd} floor lfloor frac{m}{pd} floor
    &xlongequal[quad]{T=pd}sumlimits_{T=1}^n sumlimits_{pmid T,pin pr} mu(frac{T}{p}) lfloor frac{n}{T} floor lfloor frac{m}{T} floor
    \&=sumlimits_{T=1}^n lfloor frac{n}{T} floor lfloor frac{m}{T} floor (sumlimits_{pmid T,pin pr} mu(frac{T}{p}))
    end{aligned}$$

    后面一部分可以用埃氏筛处理,即对每个素数 $p$ 枚举其倍数,分别考虑贡献即可.

    【代码】

     1 #include<bits/stdc++.h>
     2 inline int read ( void )
     3 {
     4     int x=0;char ch;
     5     while ( !isdigit(ch=getchar()) ) ;
     6     for ( x=ch^48;isdigit(ch=getchar()); ) x=(x<<1)+(x<<3)+(ch^48);
     7     return x;
     8 }
     9 const int maxn=10000000+10;
    10 int mu[maxn];long long f[maxn];
    11 std::vector<int> pr;
    12 bool flag[maxn];
    13 signed main()
    14 {
    15     mu[1]=1;
    16     for ( int i=2;i<=10000000;i++ )
    17     {
    18         if ( !flag[i] ) pr.push_back(i),mu[i]=-1;
    19         for ( int j:pr )
    20         {
    21             if ( i*j>10000000 ) break;
    22             flag[i*j]=true;mu[i*j]=-mu[i];
    23             if ( !(i%j) ) { mu[i*j]=0;break; }
    24         }
    25     }
    26     for ( int p:pr ) for ( int i=1;i*p<=10000000;i++ ) f[i*p]+=mu[i];
    27     for ( int i=2;i<=10000000;i++ ) f[i]+=f[i-1];
    28     for ( int T=read();T--; )
    29     {
    30         int n=read(),m=read();long long ans=0;
    31         if ( n>m ) std::swap(n,m);
    32         for ( int i=1,j;i<=n;i=j+1 ) j=std::min(n/(n/i),m/(m/i)),ans+=1LL*(n/i)*(m/i)*(f[j]-f[i-1]);
    33         printf("%lld
    ",ans);
    34     }
    35     return 0;
    36 }
    luogu P2257

    【例题】http://59.61.75.5:8018/problem/1219

    【题解】

    显然先用二维前缀和的思想容斥. 考虑每一部分如何计算.

    $$egin{aligned} sumlimits_{i=1}^n sumlimits_{j=1}^m [gcd(i,j)==k]
    &= sumlimits_{i=1}^{lfloor frac{n}{k} floor} sumlimits_{j=1}^{lfloor frac{m}{k} floor} [gcd(i,j)==1]
    \&=sumlimits_{i=1}^{lfloor frac{n}{k} floor} sumlimits_{j=1}^{lfloor frac{m}{k} floor} sumlimits_{dmid i,dmid j} mu(d)
    \&=sumlimits_{d=1}^{lfloor frac{n}{k} floor} mu(d)  lfloor frac{lfloor frac{n}{k} floor}{d} floor lfloor frac{lfloor frac{m}{k} floor}{d} floor
    end{aligned}$$

    【代码】

     1 #include<bits/stdc++.h>
     2 inline int read ( void )
     3 {
     4     int x=0;char ch;
     5     while ( !isdigit(ch=getchar()) ) ;
     6     for ( x=ch^48;isdigit(ch=getchar()); ) x=(x<<1)+(x<<3)+(ch^48);
     7     return x;
     8 }
     9 const int maxn=50000+10;
    10 int mu[maxn];
    11 std::vector<int> pr;
    12 bool flag[maxn];
    13 inline long long solve ( int n,int m,int x )
    14 {
    15     n/=x;m/=x;long long res=0;
    16     for ( int i=1,j;i<=n and i<=m;i=j+1 ) j=std::min(n/(n/i),m/(m/i)),res+=1LL*(mu[j]-mu[i-1])*(n/i)*(m/i);
    17     return res;
    18 }
    19 signed main()
    20 {
    21     mu[1]=1;
    22     for ( int i=2;i<=50000;i++ )
    23     {
    24         if ( !flag[i] ) pr.push_back(i),mu[i]=-1;
    25         for ( int j:pr )
    26         {
    27             if ( i*j>50000 ) break;
    28             flag[i*j]=true;mu[i*j]=-mu[i];
    29             if ( !(i%j) ) { mu[i*j]=0;break; }
    30         }
    31     }
    32     for ( int i=2;i<=50000;i++ ) mu[i]+=mu[i-1];
    33     for ( int T=read();T--; )
    34     {
    35         int a=read(),b=read(),c=read(),d=read(),k=read();
    36         printf("%lld
    ",solve(b,d,k)-solve(b,c-1,k)-solve(a-1,d,k)+solve(a-1,c-1,k));
    37     }
    38     return 0;
    39 }
    DTOJ1219

    【例题】http://59.61.75.5:8018/problem/3748

    【题解】

    考虑枚举 $gcd=x$.

    $$egin{aligned} sumlimits_{x=1}^a sumlimits_{i=1}^n sumlimits_{j=1}^m [gcd(i,j)==x] frac{ij}{gcd(i,j)}
    &= sumlimits_{x=1}^a x sumlimits_{i=1}^{lfloor frac{n}{x} floor} sumlimits_{j=1}^{lfloor frac{m}{x} floor} ij[gcd(i,j)==1]
    \&= sumlimits_{x=1}^a x sumlimits_{d=1}^{lfloor frac{n}{x} floor} d^2 mu(d) S(lfloor frac{n}{xd} floor) S(lfloor frac{m}{xd} floor)
    \&xlongequal[quad]{T=xd} sumlimits_{T=1}^n S(lfloor frac{n}{T} floor) S(lfloor frac{m}{T} floor) sumlimits_{xmid T,xleq a} frac{T^2}{x} mu(frac{T}{x})
    end{aligned}$$

    后面那部分由于 $a$ 在变化无法处理. 因此考虑离线.

    将所有询问按 $a$ 排序. 考虑如何插入每个 $x$ 的贡献. 显然式子是个前缀和类似的形式,可以枚举 $x$ 的倍数并用树状数组更新及查询.

    【代码】

     1 #include<bits/stdc++.h>
     2 const int mod=1000000007;
     3 const int maxn=100000+10;
     4 struct Query { int n,m,a,id; } Q[maxn];
     5 int mu[maxn],ans[maxn],t[maxn],q;
     6 bool flag[maxn];
     7 std::vector<int> pr;
     8 inline void add ( int x,int y ) { for ( int i=x;i<=100000;i+=i&(-i) ) (t[i]+=y)%=mod; }
     9 inline int query ( int x ) { int res=0;for ( int i=x;i;i-=i&(-i) ) (res+=t[i])%=mod;return res; }
    10 inline int S1 ( int n ) { return 1LL*n*(n+1)/2%mod; }
    11 signed main()
    12 {
    13     mu[1]=1;
    14     for ( int i=2;i<=100000;i++ )
    15     {
    16         if ( !flag[i] ) pr.push_back(i),mu[i]=-1;
    17         for ( int j:pr )
    18         {
    19             if ( i*j>100000 ) break;
    20             flag[i*j]=true;mu[i*j]=-mu[i];
    21             if ( !(i%j) ) { mu[i*j]=0;break; }
    22         }
    23     }
    24     scanf("%d",&q);
    25     for ( int i=1;i<=q;i++ ) scanf("%d%d%d",&Q[i].n,&Q[i].m,&Q[i].a),Q[i].id=i;
    26     std::sort(Q+1,Q+q+1,[](const Query &q1,const Query &q2){return q1.a<q2.a;});
    27     for ( int i=1,j=1;i<=q;i++ )
    28     {
    29         while ( j<=Q[i].a )
    30         {
    31             for ( int k=j;k<=100000;k+=j ) add(k,1LL*k*(k/j*mu[k/j]%mod+mod)%mod);
    32             j++;
    33         }
    34         int n=std::min(Q[i].n,Q[i].m),m=std::max(Q[i].n,Q[i].m);
    35         for ( int l=1,r;l<=n;l=r+1 ) r=std::min(n/(n/l),m/(m/l)),ans[Q[i].id]=(ans[Q[i].id]+1LL*(query(r)-query(l-1)+mod)*S1(n/l)%mod*S1(m/l))%mod;
    36     }
    37     for ( int i=1;i<=q;i++ ) printf("%d
    ",ans[i]);
    38     return 0;
    39 }
    DTOJ3748


    习题

    【DTOJ4729】函数

    【题解】显然 $H(d)=prodlimits_{p ext{ is prime}} g(p,d)$ 是积性函数。由二平方和定理(打表找规律)可知 $H(p^e)=3e+1\,(pequiv 1pmod 4)$,Min_25 筛(先咕着)即可.

    【代码】

     1 #include<bits/stdc++.h>
     2 long long n,m,pr[100000],tot,Pow[100000],g[100000],S1[100000][5],S2[100000][5],sum1[100000],sum2[100000];
     3 bool flag[100000];
     4 inline long long f ( long long p,long long k ) { return (p%4==1) ? 3*k+1 : 1 ; }
     5 inline long long Min_25 ( long long x,long long k )
     6 {
     7     if ( x<=1 or pr[k]>x ) return 0;
     8     long long ans=((x<=m)?sum1[x]:sum2[n/x])-Pow[k-1];
     9     for ( long long i=k;i<=tot and pr[i]*pr[i]<=x;i++ ) for ( long long v=pr[i],j=1;v*pr[i]<=x;v*=pr[i],j++ ) ans+=Min_25(x/v,i+1)*f(pr[i],j)+f(pr[i],j+1);
    10     return ans;
    11 }
    12 signed main()
    13 {
    14     long long T;
    15     for ( scanf("%lld",&T);T--; )
    16     {
    17         scanf("%lld",&n);m=(long long)sqrt(n);
    18         for ( long long i=1;i<=m;i++ ) flag[i]=true;
    19         flag[1]=false;tot=0;
    20         for ( long long i=2;i<=m;i++ )
    21         {
    22             if ( flag[i] ) pr[++tot]=i,Pow[tot]=Pow[tot-1]+((i%4==1)?4:1);
    23             for ( long long j=1;j<=tot and pr[j]*i<=m;j++ )
    24             {
    25                 flag[i*pr[j]]=false;
    26                 if ( !(i%pr[j]) ) break;
    27             }
    28         }
    29         for ( long long i=1;i<=n;i=n/(n/(i+1)) )
    30         {
    31             if ( i<=m ) for ( long long j=0;j<4;j++ ) S1[i][j]=(i-1)/4+((i-1)%4>=(j+3)%4);
    32             else for ( long long j=0;j<4;j++ ) S2[n/i][j]=(i-1)/4+((i-1)%4>=(j+3)%4);
    33             if ( i==n ) break;
    34         }
    35         for ( long long i=1;i<=tot;i++ )
    36         {
    37             long long p=pr[i],p2=p*p;
    38             for ( long long a=n;a>=p2;a=n/(n/a+1) ) for ( long long j=0;j<4;j++ )
    39                 if ( a<=m )
    40                 {
    41                     if ( a/p<=m ) S1[a][p*j%4]-=S1[a/p][j]-S1[p-1][j];
    42                     else S1[a][p*j%4]-=S2[n/(a/p)][j]-S1[p-1][j];
    43                 }
    44                 else
    45                 {
    46                     if ( a/p<=m ) S2[n/a][p*j%4]-=S1[a/p][j]-S1[p-1][j];
    47                     else S2[n/a][p*j%4]-=S2[n/(a/p)][j]-S1[p-1][j];
    48                 }
    49         }
    50         for ( long long i=1;i<=n;i=n/(n/(i+1)) )
    51         {
    52             if ( i<=m ) S1[i][1]--;
    53             else S2[n/i][1]--;
    54             if ( i==n ) break;
    55         }
    56         for ( long long i=1;i<=n;i=n/(n/(i+1)) )
    57         {
    58             if ( i<=m ) sum1[i]=4*S1[i][1]+S1[i][3]+1;
    59             else sum2[n/i]=4*S2[n/i][1]+S2[n/i][3]+1;
    60             if ( i==n ) break;
    61         }
    62         printf("%lld
    ",Min_25(n,1)+1);
    63     }
    64     return 0;
    65 }
    DTOJ4729

    【DTOJ3726】旧试题

     【题解】

    $$egin{aligned} sumlimits_{i=1}^A sumlimits_{j=1}^B sumlimits_{k=1}^C mathbf{d}(ijk)
    &= sumlimits_{i=1}^A sumlimits_{j=1}^B sumlimits_{k=1}^C sumlimits_{amid i} sumlimits_{bmid j} sumlimits_{cmid k}[a perp b][bperp c][c perp a]
    \&= sumlimits_{a=1}^A sumlimits_{b=1}^B sumlimits_{c=1}^C [a perp b][bperp c][c perp a] lfloor frac{A}{a} floor lfloor frac{B}{b} floor lfloor frac{C}{c} floor
    \&= sumlimits_{a=1}^A sumlimits_{b=1}^B sumlimits_{c=1}^C lfloor frac{A}{a} floor lfloor frac{B}{b} floor lfloor frac{C}{c} floor sumlimits_{umid a,u mid b} mu(u) sumlimits_{vmid b,v mid c}mu(v) sumlimits_{wmid c,w mid a}mu(w)
    \&= sumlimits_{u=1}^{min(A,B)} sumlimits_{v=1}^{min(B,C)} sumlimits_{w=1}^{min(C,A)} mu(u) mu(v) mu(w) sumlimits_{operatorname{lcm}(w,u)mid a} lfloorfrac{A}{a} floorsumlimits_{operatorname{lcm}(u,v)mid b} lfloorfrac{B}{b} floor sumlimits_{operatorname{lcm}(v,w)mid c} lfloorfrac{C}{c} floor
    end{aligned}$$
    设 $f(n,x)=sumlimits_{n mid d} lfloor frac{x}{d} floor$.
    $$egin{aligned} sumlimits_{i=1}^A sumlimits_{j=1}^B sumlimits_{k=1}^C mathbf{d}(ijk)
    &= sumlimits_{u=1}^{min(A,B)} sumlimits_{v=1}^{min(B,C)} sumlimits_{w=1}^{min(C,A)} mu(u) mu(v) mu(w) sumlimits_{operatorname{lcm}(w,u)mid a} lfloorfrac{A}{a} floorsumlimits_{operatorname{lcm}(u,v)mid b} lfloorfrac{B}{b} floorsumlimits_{operatorname{lcm}(v,w)mid c} lfloorfrac{C}{c} floor
    \&=sumlimits_{u=1}^{min(A,B)} sumlimits_{v=1}^{min(B,C)} sumlimits_{w=1}^{min(C,A)} mu(u) mu(v) mu(w) f(operatorname{lcm}(w,u),A)f(operatorname{lcm}(u,v),B)f(operatorname{lcm}(v,w),C)
    end{aligned}$$

    反演部分结束. 考虑如何计算上面那个式子.

    考虑怎样的三个数 $u,v,w$ 会对答案造成贡献,显然当且仅当 $mu(u) eq 0,mu(v) eq 0,mu(w) eq 0, operatorname{lcm}(w,u) < max(A,B,C), operatorname{lcm}(u,v) < max(A,B,C), operatorname{lcm}(v,w) < max(A,B,C)$ 时 $u,v,w$ 才会造成贡献.

    把上述条件拆成三个:对于 $u,v$ 有:$mu(u) eq 0,mu(v) eq 0,operatorname{lcm}(u,v) < max(A,B,C)$. 考虑建图计算三元环个数. 对于满足条件的 $u,v$. 连边 $(u,v)$,边权 $operatorname{lcm}(u,v)$.

    考虑枚举边权连边,先用埃氏筛筛出每个数的所有素因子. 考虑对于每个 $operatorname{lcm}$ 枚举子集求 $u$. 对 $v$ 枚举 $u$ 关于 $operatorname{lcm}$ 补集的超集即可. 打表证明边数为 $760741$.

    $O(m sqrt m)$ 三元环计数+卡常即可.

    【代码】

     1 #include<bits/stdc++.h>
     2 const int mod=1000000007;
     3 const int maxn=100000+10;
     4 const int maxm=1000000+10;
     5 int pr[maxn],mu[maxn],a[4],d[maxn],tot;
     6 long long f[4][maxn];
     7 bool flag[maxn];
     8 std::vector<int> F[maxn];
     9 typedef struct { int v,w; } edge;
    10 typedef struct { int u,v,w; } Edge;
    11 std::vector<edge> G[maxn];
    12 std::vector<Edge> E;
    13 inline void solve()
    14 {
    15     scanf("%d%d%d",&a[1],&a[2],&a[3]);std::sort(a+1,a+4);
    16     for ( int t=1;t<=3;t++ ) for ( int i=1;i<=a[t];i++ ) for ( int j=1;j*i<=a[t];j++ ) (f[t][i]+=a[t]/(i*j))%=mod;
    17     for ( int i=1;i<=a[3];i++ ) if ( mu[i] )
    18     {
    19         int n=(int)F[i].size(),U=(1<<n)-1;
    20         for ( int S=0;S<=U;S++ )
    21         {
    22             int u=1;
    23             for ( int j=0;j<n;j++ ) if ( S&(1<<j) ) u*=F[i][j];
    24             int C=U^S;
    25             for ( int T=C;;T=(T+1)|C )
    26             {
    27                 int v=1;
    28                 for ( int j=0;j<n;j++ ) if ( T&(1<<j) ) v*=F[i][j];
    29                 d[u]++;d[v]++;
    30                 if ( u<v ) E.push_back((Edge){u,v,i});
    31                 if ( T==U ) break;
    32             }
    33         }
    34     }
    35     long long ans=0;
    36     for ( Edge e:E )
    37     {
    38         if ( d[e.u]<d[e.v] ) std::swap(e.u,e.v);
    39         G[e.u].push_back((edge){e.v,e.w});
    40     }
    41     for ( int u=1;u<=a[3];u++ )
    42     {
    43         for ( edge i:G[u] ) for ( edge j:G[i.v] )
    44         {
    45             int v=i.v,w=j.v,A=i.w,B=j.w,C=1LL*w*u/std::__gcd(w,u);
    46             if ( C>a[3] ) continue;
    47             (ans+=mu[u]*mu[v]*mu[w]*(f[2][B]*f[3][C]+f[2][C]*f[3][B])%mod*f[1][A])%=mod;
    48             (ans+=mu[u]*mu[v]*mu[w]*(f[2][C]*f[3][A]+f[2][A]*f[3][C])%mod*f[1][B])%=mod;
    49             (ans+=mu[u]*mu[v]*mu[w]*(f[2][A]*f[3][B]+f[2][B]*f[3][A])%mod*f[1][C])%=mod;
    50             (ans+=mod)%=mod;
    51         }
    52     }
    53     for ( Edge e:E )
    54     {
    55         int x=e.u,y=e.v,z=e.w;
    56         (ans+=mu[x]*(f[1][z]*f[2][z]%mod*f[3][y]+f[1][z]*f[2][y]%mod*f[3][z]+f[1][y]*f[2][z]%mod*f[3][z]))%=mod;
    57         (ans+=mu[y]*(f[1][z]*f[2][z]%mod*f[3][x]+f[1][z]*f[2][x]%mod*f[3][z]+f[1][x]*f[2][z]%mod*f[3][z]))%=mod;
    58         (ans+=mod)%=mod;
    59     }
    60     for ( int i=1;i<=a[1];i++ ) (ans+=mu[i]*f[1][i]*f[2][i]%mod*f[3][i])%=mod,(ans+=mod)%=mod;
    61     printf("%lld
    ",ans);E.clear();
    62     for ( int i=1;i<=a[3];i++ ) G[i].clear(),d[i]=0;
    63     for ( int t=1;t<=3;t++ ) for ( int i=1;i<=a[t];i++ ) f[t][i]=0;
    64 }
    65 signed main()
    66 {
    67     mu[1]=1;
    68     for ( int i=2;i<=100000;i++ )
    69     {
    70         if ( !flag[i] ) pr[++tot]=i,mu[i]=-1;
    71         for ( int j=1;j<=tot and pr[j]*i<=100000;j++ )
    72         {
    73             flag[i*pr[j]]=true;mu[i*pr[j]]=-mu[i];
    74             if ( !(i%pr[j]) ) { mu[i*pr[j]]=0;break; }
    75         }
    76     }
    77     for ( int j=1;j<=tot;j++ ) for ( int i=1;i*pr[j]<=100000;i++ ) F[i*pr[j]].push_back(pr[j]);
    78     int T;
    79     for ( scanf("%d",&T);T--; ) solve();
    80     return 0;
    81 }
    DTOJ3726
  • 相关阅读:
    在SQL2000怎樣用動態實現SQL2005的nvarchar(max)功能
    行列互换
    c#+GUI在aspx页面画图
    做网站用UTF8还是GB2312?
    Mvc如何做权限
    表白网
    vs2008保存很慢,提速
    MVC 向View传值
    aspx画图表
    什么是MVC
  • 原文地址:https://www.cnblogs.com/RenSheYu/p/12306837.html
Copyright © 2020-2023  润新知