SPOJ NUMTRYE Number Theory (Easy) (pollard_rho分解质因数)

博客介绍了SPOJ NUMTRYE数论简单问题的解决方案,通过pollard_rho算法进行质因数分解,以避免O(n√)的时间复杂度导致超时。同时提到,对于包含大量素数的数据,可以先进行素性检验优化算法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

SPOJ NUMTRYE Number Theory (Easy)

题意:

g(n)=i=1nngcd(n,i)f(n)=i=1n(p2ki+1i+1)f(n)g(n)%1000000007.n1012

思路:

n=pk11pk22...pkss

g(n)=(p2k1+11+1)(p2k2+12+1)...(p2ks+1s+1)(p1+1)(p2+1)...(ps+1)

f(n)g(n)=(p1+1)(p2+1)(pk+1)

至于如何证明可以参考acdreamer大神的博客:
最大公约数和最小公倍数
以及这篇论文:https://cs.uwaterloo.ca/journals/JIS/VOL13/Toth/toth10.pdf

弱表示只能打表找规律啊!
知道上面规律之后呢,你直接写质因数分解 O(n) 面对10000个cases是会TLE的.
所以还得换成pollard_rho算法来分解质因数,
pollard_rho 俗称泼辣的肉 算法时间理论复杂度为 O(n14) ,乘以 O(t) 刚好够。

不过这题也有一种剪枝水过的方法。。就是数据里很多都是素数,在计算之前先进行素性检验可以优化一下。这样就可以不用写pollard_rho算法,直接质因数分解也能过。

代码:
/*
* @author FreeWifi_novicer
* language : C++/C
*/
#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<string>
#include<map>
#include<set>
#include<vector>
#include<queue>

using namespace std;

#define clr( x , y ) memset(x,y,sizeof(x))
#define cls( x ) memset(x,0,sizeof(x))
#define pr( x ) cout << #x << " = " << x << endl 
#define pri( x ) cout << #x << " = " << x << " " 
#define test( t ) int t ; cin >> t ; int kase = 1 ; while( t-- )
#define out( kase ) printf( "Case %d: " , kase++ )
#define mp make_pair
#define pii pair<int,int>
#define pb push_back
typedef long long lint;
typedef long long ll;
typedef long long LL;

const int S = 8 ;
const int mod = 1e9 + 7 ;
lint multi_mod( lint a , lint b , lint c ){
    a %= c ; b %= c ;
    lint res = 0 ;
    while( b ){
        if( b & 1 ){
            res += a ;
            if( res > c ) res -= c ;
        }
        a <<= 1 ;
        if( a > c ) a-= c ;
        b >>= 1 ;
    }
    return res ;
}

lint pow_mod( lint x , lint n , lint p ){
    x %= p ;
    lint res = 1 ;
    while( n ){
        if( n & 1 )
            res = multi_mod( res , x , p ) ;
        n >>= 1 ;
        x = multi_mod( x , x , p ) ;
    }
    return res ;
}

bool check( lint a , lint n , lint x , lint t ){
    lint res = pow_mod( a , x , n ) ;
    lint last = res ;
    for( int i = 0 ; i < t ; i++ ){
        res = multi_mod( res , res , n ) ;
        if( res == 1 && last != 1 && last != n - 1 ) return true ;
        last = res ;
    }
    if( res != 1 ) return true ;
    return false ;
}

bool Miller_Rabin( lint n ){
    if( n < 2 ) return false ;
    if( n == 2 ) return true ;
    if( ( n & 1 ) == 0 ) return false ;
    lint x = n - 1 ;
    lint t = 0 ;
    while( ( x & 1 ) == 0 ){ x >>= 1 ; t++ ; }
    srand( time( NULL ) ) ;
    for( int i = 0 ; i < S ; i++ ){
        lint a = rand() % ( n - 1 ) + 1 ;
        if( check( a , n , x , t ) )
            return false ;
    }
    return true ;
}

lint factor[100] ;
int tol ;
lint gcd( lint a , lint b ){
    return ( b == 0 )? a : gcd( b , a % b ) ;
}

lint pollard_rho( lint x , lint c ){
    lint i = 1 , k = 2 ;
    srand( time( NULL ) ) ;
    lint x0 = rand() % ( x - 1 ) + 1 ;
    lint y = x0 ;
    while( 1 ){
        i++ ;
        x0 = ( multi_mod( x0 , x0 , x ) + c ) % x ;
        lint d = gcd( ( y - x0 + x ) % x , x ) ;
        if( d != 1 && d != x ) return d ;
        if( y == x0 ) return x ;
        if( i == k ){ y = x0 ; k += k ; }
    }
}
// k = 107
void findfac( lint n , lint k ){
    if( n == 1 ) return ;
    if( Miller_Rabin(n) ){
        factor[tol++] = n ;
        return ;
    }
    lint p = n ; 
    int c = k ;
    while( p >= n )
        p = pollard_rho( p , c-- ) ;
    findfac( p , k ) ;
    findfac( n / p , k ) ;
}

lint pf[100][2] ;
int getPrifac( lint n ){
    tol = 0 ;
    findfac( n , 107 ) ;
    sort( factor , factor + tol ) ;
    cls( pf ) ;
    pf[0][0] = factor[0] ;
    pf[0][1] = 1 ;
    int k = 0 ;
    for( int i = 1 ; i < tol ; i++ ){
        if( factor[i] != factor[i-1] ) pf[++k][0] = factor[i] ;
        pf[k][1] ++ ;
    }
    return k + 1 ;
}

lint calc( lint n ){
    int len = getPrifac( n ) ;
    lint res = 1 ;
    for( int i = 0 ; i < len ; i++ ){
        res = res % mod * ( pf[i][0] + 1 ) % mod ;
    }
    return res % mod ;
}
int main(){
//  freopen("input.txt","r",stdin);
    test(t){
        lint n ;
        scanf( "%lld" , &n ) ;
        printf( "%lld\n" , calc(n) ) ;
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值