• 【简●解】POJ 1845 【Sumdiv】


    【题目大意】

    给定(A)(B),求(A^B)的所有约数之和,对(9901)取模。
    (对于全部数据,(0<= A <= B <=50,000,000)

    【算法关键词】

    • 数论
    • 综合模板
    • 二分,乘法逆元

    【分析】

    不管什么题首先思考的肯定是暴力解法。起码可以骗分啊,当然,如果能一眼标算,那再好不过了。
    这道题暴力做法就不说了,其实仔细思考也不会真的打暴力吧。。。

    看见约数,首先想到的应该就是数论的有关知识。那么这道题实际上就是在求(A^B)的约数之和,问题的难点就在于求解答案的时间优化。

    首先,可以思考唯一分解定理,即任意一个自然数都可分解且只能分解成以下形式:

    [n=p_1^{k_1}*p_2^{k_2}*p_3^{k_3}*...*p_m^{k_m} ]

    其中,(p)为质因数,(k)为自然数。

    那如何实现呢?

    首先通过线性筛把题目范围内的质数筛出来存在一个数组里,然后枚举数组里的质数是否能被(A)整除,即枚举(p),如果能,就以(p)为底枚举(k),并存入相对应的数组。

    于是就可分解为:

    [A^B=p_1^{k_1*B}*p_2^{k_2*B}*p_3^{k_3*B}*...*p_m^{k_m*B} ]

    (a)所有约数和为(ans(a))
    对于两个互质的数(a),(b),必定有(ans(ab)=ans(a)*ans(b)):

    (a)约数为(x_1),(x_2)...(x_m)(b)约数为(y_1),(y_2),...(y_n)
    (a),(b)互质,(a_1->a_m)(b_2->b_n)无公共元素
    那么

    [ans(ab)=sum_{i=1,j=1}^{i<=m,j<=n}x_i*y_j\ quad=sum_{i=1}^{m}x_i*sum_{j=1}^{n}y_j\ =ans(a)*ans(b) ]

    得证 这不是积性函数的性质吗

    同理,(p_i^{k_i*B})(p_j^{k_j*B})互质
    所以:

    [ans(A^B)=prod_{i=1}^msum_i ]

    其中:

    [sum_i=sum_{j=0}^{k_i*B}p_i^j ]

    即:

    [ans(A^B)=prod_{i=1}^{m}sum_{j=0}^{k_i*B}p_i^j ]

    (理解不透彻的可以把这个拆开看看)

    公式推到这里,其实就可以写代码了,在这里,还要注意(sum_i)的计算。

    [sum_i=p_i^0+p_i^1+p_i^2+...+p_i^{k_i} ]

    这里有两种方法。

    1. 二分+递归思想
      (k_i)为奇数:

    [sum(p, k) = sum(p, k/2)*(1+p^{n/2+1}) ]

    (k_i)为偶数:

    [sum(p, k) = sum(p, k/2-1)*(1+p^{n/2+1})+p^{n/2} ]

    证明的话可以把上面的式子拆开来看,这里不再赘述。

    1. 等比数列通项公式

    [sum(p, k) = frac{p^{k+1}-1}{p-1} ]

    当然,如果在模的意义下这样做就需要乘法逆元的相关知识,这里就只给出法一的代码。法二的代码可以自己尝试下。其实是因为我懒

    #include<cmath>
    #include<ctime>
    #include<queue>
    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    
    #define debug() puts("FBI WARNING!")
    #define ll long long
    #define R register
    
    using namespace std;
    
    /*常量申明*/ 
    const int MAX = 500000;
    const int BIG_SEVEN = 777777;
    const int MEIZI = 100000;
    const int mod = 9901;
    /*函数申明*/ 
    void ola(); void test(); void divide(); void cls();void solve();
    ll binary_pow(ll, ll); ll sum(ll, ll); ll read();
    /*变量申明*/ 
    ll a, b, s, prim[BIG_SEVEN], p[MEIZI], k[MEIZI];
    bool vis[MAX + 5];
    int cnt, tot;
    
    int main(){
        ola();
        while (~scanf("%lld %lld", &a, &b)) {
            cls();
            divide();
            solve();
            //test();
        }
        return 0;
    }
    
    inline ll read(){ // 快读 (貌似没用上) 
        ll f = 1, x = 0;char ch;
        do { ch = getchar(); if (ch == '-') f = -1; } while (ch < '0'||ch>'9');
        do {x = x*10+ch-'0'; ch = getchar(); } while (ch >= '0' && ch <= '9'); 
        return f*x;
    }
    
    inline void ola() { // 欧拉筛 
        vis[1] = 1;
        for (R int i = 2;i < MAX; ++i) {
            if (!vis[i]) {
                prim[cnt++] = i;
            }   
            for (R int j = 0;j < cnt ; ++j) {
                if (i*prim[j] > MAX) break;
                vis[i*prim[j]] = 1;
                if (i%prim[j] == 0) break;
            }
        }
    }
    
    inline void cls() {tot = 0, cnt = 0;} // 多组数据初始化  
    
    inline void divide() { // 唯一分解定理  
        memset(k, 0, sizeof(k));ll rose = a;
        for (R int i = 0; prim[i] <= rose/prim[i]; ++i) {
            if (rose%prim[i] == 0) {
                p[tot] = prim[i];
                while (rose%prim[i] == 0) {
    //              debug();
                    k[tot]++;
                    rose /= prim[i];
                }
                tot++;
            }
        }
        if (rose != 1) {
            p[tot] = rose;
            k[tot++] = 1;
        }
    }
    
    inline void solve() { // 累加答案 
        ll ans = 1;
        for (R int i = 0; i < tot; ++i) {
            ans *= (sum(p[i], b*k[i]) %mod);
            ans %= mod;
        }
        printf("%lld
    ", ans);
    }
    
    inline void test() { // 测试函数 
    /*check ola()*/
        for (R int i = 0;i < cnt; ++i) {
            cout << prim[i] << " ";
        }
        printf("%.2lf
    ",(double)clock()/CLOCKS_PER_SEC);
    
    /*check divide()*/
        for (R int i = 0;i < tot; ++i) {
            printf("%lld^%lld
    ", p[i], k[i]); 
        }
        cout << tot;
    }
    
    inline ll binary_pow(ll a, ll b) { // 二进制实现的快速模幂 
        ll ans = 1, tmp = a%mod;
        while (b) {
            if (b&1) {ans *= tmp; ans %= mod;}
            b >>= 1;
            tmp *= tmp; tmp %= mod;
        }
        return ans;
    }
    
    inline ll sum(ll p, ll k) { // 约数和二分实现(p^0+p^1+...+p^k) 
        if (p == 0) return 0;
        if (k == 0) return 1;
        if (k & 1) return ((1+binary_pow(p, k/2+1)) %mod * sum(p, k/2) %mod) %mod; // 奇数  
        else return ((1+binary_pow(p, k/2+1)) %mod*sum(p, k/2-1) + binary_pow(p, k/2) %mod) %mod;
    }
    
  • 相关阅读:
    Android长方形图片生成正圆形,以及矩形图片生成圆角
    android应用编译失败 ResXMLTree_node size 类错误,以及 android studio 项目内搜索
    Android 不能返回 parent Activity 的问题
    如何让有物理键的手机在ActionBar始终显示更多菜单menu键
    Android抽屉效果 DrawerLayout 入门经验总结
    Android使用Home键后应用程序重启的问题
    汉王云名片识别(SM)组件开发详解
    [deviceone开发]-购物车的简单示例
    [DeviceOne开发]-白板的示例
    [DeviceOne开发]-地区选择
  • 原文地址:https://www.cnblogs.com/silentEAG/p/10385915.html
Copyright © 2020-2023  润新知