1. 程式人生 > >bzoj4869 [Shoi2017]相逢是問候

bzoj4869 [Shoi2017]相逢是問候

RM problems 共有m個 mit info dmi tput 代碼 printf

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]相逢是問候