嘘~ 正在从服务器偷取页面 . . .

洛谷P4691 Nephren Ruq Insania 题解


洛谷P4691 Nephren Ruq Insania 题解

本题和 P3934 [Ynoi2016] 炸脖龙 I 完全一致

题目链接:P4691 Nephren Ruq Insania(本题没有数据,需要在 P3934 提交)

题意

奈芙莲·卢可·印萨尼亚 (Nephren-Ruq-Insania)

威廉想要救下奈芙莲,但是他自己也已经处于崩溃的边缘。

冥冥之中他想起了曾经学习过的一种魔法。在这最后一刻,或许已经是唯一的办法了。

这种魔法操作的对象是一个咒语组成的序列,每一个单独的咒语拥有自己的法力值。

威廉需要不断地按照之前的记忆,对某一段区间的法力值加上一个数,或者求出某一段区间的法咒共鸣。

法咒共鸣具体而言是这么计算的:

对于区间 $[l,r]$ , 查询

一直到 $\mathrm{pow}(a_r,\,1)$ 。其中 $\mathrm{pow}(a,b)$ 表示 $a^b$ 。

请注意每次的模数不同。

时间不够了,威廉自己无力计算这么复杂的魔法。但是这是最后的希望了。

能否拯救奈芙莲,就靠您了。

输入格式

第一行两个整数 $n,m$ ,表示序列长度和操作数。

接下来一行,$n$ 个整数 $a_i$ 表示这个序列。

接下来 $m$ 行,可能是以下两种操作之一:

  • 1 l r x ,表示区间 $[l,r]$ 加上 $x$ 。

  • 2 l r p ,表示进行一次法咒共鸣,模数为 $p$ 。

输出格式

对于每个操作 2,输出一行表示答案。

数据范围

$1\le n , m \le 5\times 10^5$ ,序列中每个数在 $[1,2\times 10^9]$ 内。

$1\le p \le 2 \times 10^7$, 每次加上的数在 $[0,2\times 10^9]$ 内。

听说这个数据结构叫奈芙莲树,难评。

这道题主要利用的就是 $\mathcal{O}(\varphi^{*}(x)=1) = \mathcal{O}(\log x)$ ,参考 P3747 相逢是问候

另外防止大家不理解,贴一下扩展欧拉定理

不过这道题底数不固定,没法光速幂预处理。

如果直接递归 $\mathrm{pow}$ 的话,我们没法判断 $b$ 是否小于 $\varphi(p)$ 。

不过注意到

也就是说,我们可以暴力算 $4$ 次 $\mathrm{pow}$ ,如果这个值小于 $\varphi(p)$ ,就返回 $\mathrm{pow}(a_l,b)$ ,否则递归。

说了半天,区间加法怎么办呢?我们可以用树状数组维护差分数组的方式来实现。

不过注意到如果每次都要查树状数组会有很大的常数,我们可以用类似于记忆化的方式存下当前的值。

时间复杂度 $\mathcal{O}(n \log n \log p)$

代码:

#include <bits/stdc++.h>
using namespace std;
// #define int long long
// #define INF 0x3f3f3f3f3f3f3f3f
typedef long long ll;
void up(int &x, int y) { x < y ? x = y : 0; }
void down(int &x, int y) { x > y ? x = y : 0; }
#define N ((int)(5e5 + 15))
#define M ((int)(2e7 + 15))

char ck[M];
int n, id, pcnt, pri[M / 10], phi[M]; ll tr[N];
int qpow(ll a, ll b, int p)
{
    ll r = 1;
    for(a %= p; b; b >>= 1, a = a * a % p)
        if(b & 1) r = r * a % p;
    return r;
}
int lowbit(int x) { return x & (-x); }
void add(int x, ll v) { ++id; for(int i = x; i <= n; i += lowbit(i)) tr[i] += v; }
ll qry(int x, ll r = 0) { for(int i = x; i; i -= lowbit(i)) r += tr[i]; return r; }
void Euler(int _ = M - 5)
{
    phi[1] = 1; int *p = pri;
    for(int i = 2; i <= _; i++)
    {
        if(!ck[i]) { p[++pcnt] = i, phi[i] = i - 1; }
        for(int j = 1; j <= pcnt && p[j] * i <= _; j++)
        {
            const int pos = p[j] * i; ck[pos] = true;
            if(i % p[j]) { phi[pos] = phi[i] * phi[p[j]]; }
            else { phi[pos] = phi[i] * p[j]; break; }
        }
    }
}
struct Arr
{
    ll v[N]; int w[N];
    ll& operator[](int x)
    { 
        if(w[x] == id) return v[x];
        w[x] = id; v[x] = qry(x); return v[x];
    }
} a;
void update(int l, int r, int x) { add(l, x); add(r + 1, -x); }
ll solve(int l, int r, int p)
{
    if(a[l] % p == 0) return 0;
    if(l == r) return a[l] % p + (a[l] >= p ? p : 0);
    int k = min(l + 5, r);
    for(int i = l + 1; i <= k; i++) if(a[i] == 1) { k = i; break; }
    ll t = 0, b = a[k];
    for(int i = k - 1; i > l; i--)
        for(t = b, b = 1; t--; ) {
            b *= a[i]; if(b > phi[p]) return qpow(a[l], solve(l + 1, r, phi[p]) + phi[p], p);
        }
    return qpow(a[l], b, p);
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0); cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    int q; cin >> n >> q; id = 0; Euler();
    for(int i = 1; i <= n; i++) { cin >> a[i], add(i, a[i] - a[i - 1]); }
    for(int i = 1, op, l, r, x; i <= q; i++)
    {
        cin >> op >> l >> r >> x;
        if(op == 1) update(l, r, x);
        else cout << (x == 1 ? 0 : solve(l, r, x) % x) << '\n';
    }
    return 0;
}

参考文献

[1] https://www.luogu.com.cn/article/vu9gjdd8


题外话

下面是题目背景,稍微修了一下格式。

奈芙莲·卢可·印萨尼亚(Nephren-Ruq-Insania)

同为妖精仓库的成体妖精兵,天赋不如珂朵莉一般,只是一个平凡的妖精。

睡觉时如同毯子一般在威廉身上为其保暖。

习惯于粘着威廉,在梦境中与艾尔梅莉亚交谈时,自称就像是威廉的宠物一样。

她只是一个非常普通的黄金妖精。

在援救打捞队的作战中,他们不幸与(几乎是所有的)第六兽相遇了。

此时的珂朵莉因为接触到星神艾露可本体,正处于昏迷之中。而威廉也无法离开珂朵莉。

默默守护在房间外的她,提起圣剑,走向了战场。

作为本身天赋只是一般的妖精少女,她难以对抗无数倍于自己的六号兽。

没有多久,她开始体力不支。

终于,在源源不断的六号兽面前,她难以抵挡了……

终于,由于魔力过度激发,她已经处在了魔力失控的边缘……

“威廉,拯救,是我们黄金妖精的使命。”

“况且,威廉之前已经救过我们了。”

“所以,已经没有问题了。”


文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录