zoukankan      html  css  js  c++  java
  • HDU5152 线段树 + 数论

      题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5152 ,线段树区间更新 + 点更新 + 数论知识(数论是重点QAQ),好题值得一做。

      BestCoder Round #24的C题,一道神题,不得不说,出题人的数论学的很好,很多人都没想到2333333不是素数的问题,当时全场爆零。我今天下午开始研究这道题,后来看了好久的标程才懂,惭愧。


      一共有两个操作一个询问:1.询问[l , r]区间里的值的和; 2.将点x的值a[x]赋为2a[x]; 3.将区间[l , r]都加上x。

      这道题复杂就是操作2了,这里需要先知道一个数论知识:  当x >= Phi(C)时, A^x = A ^ (x%Phi(C) + Phi(C)) (mod C). Phi(C)是C的欧拉函数,即1 ~ C中与C互素整数的个数,具体求法百度之。所以当操作2一直累积下去的时候应该是这样:

               

      所以就是一直迭代求2333333的欧拉函数,对于2333333这个模数来说,求18次欧拉函数后就变成了1,所以保存到19层即可。接下来就是线段树的更新与求和。

    具体解法:

      这里只介绍操作2的部分:标程中用了一个一维的vector向量来保存两个信息:每个位置的操作2的次数和操作3要加的数,具体实现方法是vector <LL> a[N]后,如果i号位置需要进行操作2,则进行操作a[i].pushback(0),如果操作3的PushDown()更新到i的位置时,则a[i][a[i].size() - 1] += add[rt]; 表示在a[i][]数组的最后一个位置加上要加的数。这样的话a[i][]数组的长度就表示有多少次操作2,保存的值就代表了当时的操作3加上的值,所以每次迭代应该是num = 2num % phi[pos] + phi[pos] + a[...] (说点有点乱,不好意思~)。

      还有一点要注意的是x < Phi(C)的情况,这样的话A ^ x 还等于 A ^ x,这样的话迭代就变为 num = 2num + a[...]. 这时候再判断num与当前层的欧拉函数的大小关系,到满足条件时再像公式中的进行即可。这里要注意一点,如果当前层满足 x >= Phi(C)的情况,则以后的每一层迭代结果必然也满足,因为 x % Phi(C) + Phi(C) 必然要大于Phi(C),这样传递下去就可以保证以后的都满足了...(- - ..还是说的很乱,具体见代码,虽然都是看的标程编的QAQ)。

    #include <iostream>
    #include <cstdio>
    #include <vector>
    #include <cmath>
    #include <string>
    #include <string.h>
    #include <algorithm>
    using namespace std;
    #define LL __int64
    #define eps 1e-8
    #define INF INT_MAX
    #define lson l , m , rt << 1
    #define rson m + 1 , r , rt << 1 | 1
    const int MOD = 2333333; 
    const int maxn = 50000 + 5;
    int phi[20] = {2333333 , 2196720 , 580608 , 165888 , 55296 , 18432 , 6144 , 
                2048 , 1024 , 512 , 256 , 128 , 64 , 32 , 16 , 8 , 4 , 2 , 1};
    LL sum[maxn << 2] , add[maxn << 2];
    vector <LL> a[maxn];
    int pow2[33];
    
    void init()
    {                //求2 ^ i
        for(int i = 0 ; i <= 30 ; i++)
            pow2[i] = 1 << i;
    }
    LL pow_mod(LL a , LL i , LL n)
    {                // a ^ i % n的快速幂
        if(i == 0)
            return 1;
        LL tmp = pow_mod(a , i >> 1 , n);
        tmp = tmp * tmp % n;
        if(i & 1)
            tmp = tmp * a % n;
        return tmp;
    }
    void PushUp(int rt)
    {
        sum[rt] = (sum[rt << 1] + sum[rt << 1 | 1]) % MOD;
    }
    void build(int l , int r , int rt)
    {
        add[rt] = 0;
        if(l == r) {
            a[l].clear();                //清零不要忘了
            scanf("%d" , &sum[rt]);
            a[l].push_back(sum[rt]);    //初始值放入a[l][0]
            sum[rt] %= MOD;
            return;
        }
        int m = (l + r) >> 1;
        build(lson);
        build(rson);
        PushUp(rt);
    }
    void PushDown(int rt , int len)
    {
        if(add[rt]) {
            add[rt << 1] += add[rt];
            add[rt << 1 | 1] += add[rt];
            sum[rt << 1] = (sum[rt << 1] + 1LL * (len - (len >> 1)) * add[rt]) % MOD;
            sum[rt << 1 | 1] = (sum[rt << 1 | 1] + 1LL * (len >> 1) * add[rt]) % MOD;
            add[rt] = 0;
        }
    }
    void update(int L , int R , int x , int l , int r , int rt)
    {
        if(L <= l && R >= r) {
            sum[rt] = (sum[rt] + 1LL * (r - l + 1) * x) % MOD;
            add[rt] += x;
            return;
        }
        PushDown(rt , r - l + 1);
        int m = (l + r) >> 1;
        if(L > m)
            update(L , R , x , rson);
        else if(R <= m)
            update(L , R , x , lson);
        else {
            update(L , R , x , lson);
            update(L , R , x , rson);
        }
        PushUp(rt);
    }
    int cal(vector <LL> a)
    {    
        LL num;
        if(a.size() < 19) {        //没到18层,所以要全部来一遍
            num = a[0];
            bool flag = false;    //flag判断是否满足 x >= Phi(C)
            int pos = a.size() - 1;
            if(num >= phi[pos]) {
                flag = true;
                num = num % phi[pos] + phi[pos];
            }
            pos--;
            for(int i = 1 ; i < a.size(); i++ , pos--) {
                if(flag) {
                    num = (pow_mod(2 , num , phi[pos]) + a[i]) % phi[pos] + phi[pos];
                } else {
                    if(num >= 30) {
                        flag = true;
                        num = (pow_mod(2 , num , phi[pos]) + a[i]) % phi[pos] + phi[pos];
                    } else {
                        num = pow2[num] + a[i];        //这时就是 2 ^ num + a[i]
                        if(num >= phi[pos]) {
                            flag = true;
                            num = num % phi[pos] + phi[pos];
                        }
                    }
                }
            }
        } else {        //由于Phi[18]就等于1了,所以之前取余就都等于0,不管就可以了
            num = 1;
            int pos = 17;
            for(int i = a.size() - 18 ; i < a.size() ; i++ , pos--) {
                num = (pow_mod(2 , num , phi[pos]) + a[i]) % phi[pos] + phi[pos];
            }
        }
        return num % MOD;
    }
    void modify(int p , int l , int r , int rt)
    {
        if(l == r) {
            if(add[rt]) {    //保留操作3的信息
                a[p][a[p].size() - 1] += add[rt];
                add[rt] = 0;
            }
            a[p].push_back(0);    //加一层
            sum[rt] = cal(a[p]);
            return;
        }
        PushDown(rt , r - l + 1);
        int m = (l + r) >> 1;
        if(p <= m)
            modify(p , lson);
        else
            modify(p , rson);
        PushUp(rt);
    }
    int query(int L , int R , int l , int r , int rt)
    {
        if(L <= l && R >= r) {
            return sum[rt] % MOD;
        }
        PushDown(rt , r - l + 1);
        int m = (l + r) >> 1;
        if(L > m)
            return query(L , R , rson);
        else if(R <= m)
            return query(L , R , lson);
        else 
            return (query(L , R , lson) + query(L , R , rson)) % MOD;
    }
    int main()
    {
        init();
        int a , b , c , n , m , ch;
        while(~scanf("%d %d" , &n , &m))
        {
            build(1 , n , 1);
            while(m--) {
                scanf("%d" , &ch);
                if(ch == 1) {
                    scanf("%d %d" , &a , &b);
                    printf("%d
    " , query(a , b , 1 , n , 1));
                } else if(ch == 2) {
                    scanf("%d" , &c);
                    modify(c , 1 , n , 1);
                } else {
                    scanf("%d %d %d" , &a , &b , &c);
                    update(a , b , c , 1 , n , 1);
                }
            }
        }
        return 0;
    }

    另附欧拉函数的代码:

    int Euler(int n) 
    {
        vector <int> a;
        int i = 2;
        int res = n;
        while(n != 1) {
            if(n % i == 0)
                a.push_back(i);
            while(n % i == 0) 
                n /= i;
            i++;
        }
        for(i = 0 ; i < a.size() ; i++)
            res = res / a[i] * (a[i] - 1);
        return res;
    }
  • 相关阅读:
    Struts2学习总结——文件上传与下载
    String.StartsWith与 EndsWith在大量使用时效率果然极低
    使用CXF实现基于Rest方式的WebService
    使用CXF实现基于Soap协议的WebService
    Hibernate单向“一对多”关联
    Hibernate单向“多对一”关联
    Hibernate单向“一对一”关联
    Spring使用经验之Listener综述
    Eclipse3.4以上使用dropins的插件安装方式
    数据库第一、二、三范式
  • 原文地址:https://www.cnblogs.com/H-Vking/p/4312177.html
Copyright © 2011-2022 走看看