5069. 蛋糕

来源:互联网 发布:罗马圈窗帘打孔的算法 编辑:程序博客网 时间:2024/04/27 20:29

题目大意

求在n×n的网格图中彼此可见的点之间的线段总数。

Data Constraint
n109

题解

题目要求的就是

2[2×n2i=1nφ(i)3ni=1nφ(i)×i+i=1nφ(i)×i2]

然后就可以考虑用杜教筛来计算。

1.计算S(n)=ni=1φ(i)
对于φ,具有d|iφ(d)=i的性质。
那么

i=1nd|iφ(d)=n(n+1)2

先枚举约数,转化一下
i=1nj=1niφ(j)=n(n+1)2

然后套路一波
S(n)+i=2nS(ni)=n(n+1)2

2.计算S(n)=ni=1φ(i)×i
因为d|iφ(d)=i,所以d|iφ(d)×d×id=i2

i=1nd|iφ(d)×d×id=i=1ni2=n(n+1)(2n+1)6

i=1nj=1niφ(j)×i×j=n(n+1)(2n+1)6

S(n)+i=2ni×S(ni)=n(n+1)(2n+1)6

3.计算S(n)=ni=1φ(i)×i2与第二种类似。
最后化出来

S(n)+i=2ni2×S(ni)=(n(n+1)2)2

SRC

#include<cstdio>#include<cstdlib>#include<cstring>#include<iostream>#include<algorithm>#include<cmath>using namespace std ;#define N 10000000 + 10#define M 40000 + 10typedef long long ll ;const int MAXN = 5e6 ;ll Q[3][M] ;bool flag[N] ;int Pri[N] , phi[N] ;ll Sum[3][N] ;int n , MO , Block ;void Pre() {    phi[1] = 1 ;    for (int i = 2 ; i <= MAXN ; i ++ ) {        if ( !flag[i] ) {            Pri[++Pri[0]] = i ;            phi[i] = i - 1 ;        }        for (int j = 1 ; j <= Pri[0] ; j ++ ) {            if ( 1ll * i * Pri[j] > MAXN ) break ;            flag[i*Pri[j]] = 1 ;            if ( i % Pri[j] == 0 ) {                phi[i*Pri[j]] = phi[i] * Pri[j] ;                break ;            }            phi[i*Pri[j]] = phi[i] * (Pri[j] - 1) ;        }    }    for (int i = 1 ; i <= MAXN ; i ++ ) {        Sum[0][i] = (Sum[0][i-1] + phi[i]) % MO ;        Sum[1][i] = (Sum[1][i-1] + (ll)i * phi[i] % MO) % MO ;        Sum[2][i] = (Sum[2][i-1] + (ll)i * i % MO * phi[i] % MO) % MO ;    }}ll Calc( int k , ll n ) {    if ( k == -1 ) return n % MO ;    ll ret ;    if ( k != 1 ) ret = (n * (n + 1) / 2) % MO ;    else {        ll ret1 = n , ret2 = n + 1 , ret3 = 2 * n + 1 ;        if ( ret1 % 2 == 0 ) ret1 /= 2 ;        else if ( ret2 % 2 == 0 ) ret2 /= 2 ;        else if ( ret3 % 2 == 0 ) ret3 /= 2 ;        if ( ret1 % 3 == 0 ) ret1 /= 3 ;        else if ( ret2 % 3 == 0 ) ret2 /= 3 ;        else if ( ret3 % 3 == 0 ) ret3 /= 3 ;        ret1 = ret1 * ret2 >= MO ? ret1 * ret2 % MO : ret1 * ret2 ;        ret1 = ret1 * ret3 >= MO ? ret1 * ret3 % MO : ret1 * ret3 ;        return ret1 ;    }    if ( k == 2 ) ret = ret * ret >= MO ? ret * ret % MO : ret * ret ;    return ret ;}ll S( int x , int k ) {    if ( x <= MAXN ) return Sum[k][x] ;    int t = x <= Block ? x : n / x + Block ;    if ( Q[k][t] ) return Q[k][t] ;    ll ret = Calc( k , x ) ;    for (int i = 2 ; i <= x ; i ++ ) {        int r = x / (x / i) ;        ret = (ret - (Calc( k - 1 , r ) - Calc( k - 1 , i - 1 )) % MO * S(x/i,k) % MO) % MO ;        i = r ;    }    Q[k][t] = ret ;    return ret ;}int main() {    freopen( "cake.in" , "r" , stdin ) ;    freopen( "cake.out" , "w" , stdout ) ;    scanf( "%d%d" , &n , &MO ) ;    Block = sqrt(n) ;    Pre() ;    ll ans = (ll)n * n % MO * 2ll % MO * S(n,0) % MO ;    ans = (ans - 3ll * n % MO * S(n,1) % MO) % MO ;    ans = (ans + S(n,2)) % MO ;    ans = 2ll * ans % MO ;    printf( "%lld\n" , (ans + MO) % MO ) ;    return 0 ;}

以上.

1 0