bzoj4869 [Shoi2017]相逢是问候

Posted zbtrs

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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\\n",query(1,1,n,l,r));
    }

    return 0;
}

 

   

以上是关于bzoj4869 [Shoi2017]相逢是问候的主要内容,如果未能解决你的问题,请参考以下文章

bzoj4869 [Shoi2017]相逢是问候

BZOJ4869:[SHOI2017]相逢是问候——题解

Bzoj4869: [Shoi2017]相逢是问候

bzoj4869 [Shoi2017]相逢是问候

bzoj 4869: [Shoi2017]相逢是问候

bzoj4869[Shoi2017]相逢是问候 线段树+扩展欧拉定理