• bzoj4869 [Shoi2017]相逢是问候


    4869: [Shoi2017]相逢是问候

    Time Limit: 40 Sec  Memory Limit: 512 MB
    Submit: 1311  Solved: 470
    [Submit][Status][Discuss]

    Description

    Informatikverbindetdichundmich.
    信息将你我连结。B君希望以维护一个长度为n的数组,这个数组的下标为从1到n的正整数。一共有m个操作,可以
    分为两种:0 l r表示将第l个到第r个数(al,al+1,...,ar)中的每一个数ai替换为c^ai,即c的ai次方,其中c是
    输入的一个常数,也就是执行赋值ai=c^ai1 l r求第l个到第r个数的和,也就是输出:sigma(ai),l<=i<=rai因为
    这个结果可能会很大,所以你只需要输出结果mod p的值即可。

    Input

    第一行有三个整数n,m,p,c,所有整数含义见问题描述。
    接下来一行n个整数,表示a数组的初始值。
    接下来m行,每行三个整数,其中第一个整数表示了操作的类型。
    如果是0的话,表示这是一个修改操作,操作的参数为l,r。
    如果是1的话,表示这是一个询问操作,操作的参数为l,r。
    1 ≤ n ≤ 50000, 1 ≤ m ≤ 50000, 1 ≤ p ≤ 100000000, 0 < c <p, 0 ≤ ai < p

    Output

    对于每个询问操作,输出一行,包括一个整数表示答案mod p的值。

    Sample Input

    4 4 7 2
    1 2 3 4
    0 1 4
    1 2 4
    0 1 4
    1 1 3

    Sample Output

    0
    3

    HINT

     鸣谢多名网友提供正确数据,已重测!

    Source

    黑吉辽沪冀晋六省联考&&鸣谢xlk授权本OJ使用权

    分析:比较难的一道题.

       如果做过bzoj3884,就会想到用欧拉定理来降幂.  和区间开根号一样,一个数操作有限次数后就会变成一个常数. 在它变成常数以前对它暴力修改即可. 为什么会变成一个常数呢? p --> phi(p) --> phi(phi(p)) ...... 最后一定会变成1. 不论什么数mod 1都等于0.

       区间开根号可以用分块来做,那么这道题能不能用分块来做呢?显然是不行的,复杂度太高! 每个数会被暴力修改log次,每次修改需要对log个phi求快速幂,快速幂的复杂度也是log的,也就是说:将一个数修改到底的复杂度是O(log^3n)的,显然是不能接受的. 既然不能分块,用线段树做就好了.

       令p通过不断取phi变成1的次数为cnt. 如果要修改一个区间[l,r],若当前区间的所有元素的最少操作次数≥cnt,这个区间的元素就不需要被修改了. 否则暴力修改.

       一开始将每一层的phi给记录下来(模数). 如果要修改第i个元素,第i个元素已经被修改tot次了,那么就从第tot + 1层到1层逐层计算答案.  需要注意的是:扩展欧拉定理只能在幂次≥phi的时候才能使用,在快速幂的时候判断一下就好了.

       注意:一定要展开phi(1) = 1那一层. p=3,c=2的话,整个序列只有一个数字,一开始是0,接着不断进行修改操作和查询操作。
    错误的代码,会返回0->1->2->2->2->.... (p=3,c=2),但是事实上应该是0->1->2->1->1->....

    #include <cmath>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    typedef long long ll;
    const ll maxn = 50010;
    ll n,m,P,c,a[maxn],p[maxn],cnt,sum[maxn << 2],minn[maxn << 2];
    
    ll phi(ll x)
    {
        ll res = x;
        for (ll i = 2; i <= sqrt(x); i++)
        {
            if (x % i == 0)
            {
                while (x % i == 0)
                    x /= i;
                res = res / i * (i - 1);
            }
        }
        if (x > 1)
            res = res / x * (x - 1);
        return res;
    }
    
    void pre()
    {
        ll t = P;
        p[0] = P;
        while (t != 1)
        {
            p[++cnt] = phi(t);
            t = p[cnt];
        }
        p[++cnt] = 1;
    }
    
    void pushup(ll o)
    {
        sum[o] = sum[o * 2] + sum[o * 2 + 1];
        sum[o] %= p[0];
        minn[o] = min(minn[o * 2],minn[o * 2 + 1]);
    }
    
    void build(ll o,ll l,ll r)
    {
        if (l == r)
        {
            sum[o] = a[l] % p[0];
            return;
        }
        ll mid = (l + r) >> 1;
        build(o * 2,l,mid);
        build(o * 2 + 1,mid + 1,r);
        pushup(o);
    }
    
    ll qpow(ll a,ll b,ll pp,bool &flag)
    {
        flag = false;
        ll res = 1;
        while (b)
        {
            if (b & 1)
            {
                if (res * a >= pp)
                    flag = true;
                res = (res * a) % pp;
            }
            if (b != 1 && a * a >= pp)
                flag = true;
            a = (a * a) % pp;
            b >>= 1;
        }
        return res;
    }
    
    ll calc(ll x,ll ph)
    {
        ll res = x;
        bool flag;
        if (res >= p[ph])
            res = res % p[ph] + p[ph];
        while (ph--)
        {
            x = res;
            res = qpow(c,x,p[ph],flag);
            if (flag)
                res += p[ph];
        }
        //cout << res << endl;
        return res % p[0];
    }
    
    void update(ll o,ll l,ll r,ll x,ll y)
    {
        if (minn[o] >= cnt)
            return;
        if (l == r)
        {
            minn[o]++;
            sum[o] = calc(a[l],minn[o]);
            return;
        }
        ll mid = (l + r) >> 1;
        if (x <= mid)
            update(o * 2,l,mid,x,y);
        if (y > mid)
            update(o * 2 + 1,mid + 1,r,x,y);
        pushup(o);
    }
    
    ll query(ll o,ll l,ll r,ll x,ll y)
    {
        if (x <= l && r <= y)
            return sum[o];
        ll mid = (l + r) >> 1,res = 0;
        if (x <= mid)
            res += query(o * 2,l,mid,x,y);
        res %= p[0];
        if (y > mid)
            res += query(o * 2 + 1,mid + 1,r,x,y);
        res %= p[0];
        return res;
    }
    
    int main()
    {
        scanf("%lld%lld%lld%lld",&n,&m,&P,&c);
        for (ll i = 1; i <= n; i++)
            scanf("%lld",&a[i]);
        pre();
        build(1,1,n);
        for (ll i = 1; i <= m; i++)
        {
            ll opt,l,r;
            scanf("%lld%lld%lld",&opt,&l,&r);
            if (opt == 0)
                update(1,1,n,l,r);
            else
                printf("%lld
    ",query(1,1,n,l,r));
        }
    
        return 0;
    }

       

  • 相关阅读:
    【】Libevent源码解析
    sftp使用
    世界boss设计
    记一次薪酬谈判的教训 .
    一些常用的文件操作代码
    一位总经理的辞职信,以及回复
    JMeter安装、文档参考
    Charles——charles代理菜单proxy总结——external proxy 外部代理设置
    JDK安装
    Charles——charles常用功能——重定向
  • 原文地址:https://www.cnblogs.com/zbtrs/p/8646170.html
Copyright © 2020-2023  润新知