题意

给出一个长度为 \(n\) 的序列 \(\{a_i\}\) 以及一个数 \(p\) ,现在有 \(m\) 次操作,每次操作将 \([l, r]\) 区间内的 \(a_i\) 变成 \(c^{a_i}\) 。

或者询问 \([l, r]\) 之间所有 \(a_i\) 的和对 \(p\) 取模的结果 。

\(n, m \le 5 \times 10^4, p \le 2^{14}\)

题解

考虑欧拉降幂(扩展欧拉定理),不会的可以看 这篇博客

然后对于这些不断叠加的指数,有如下式子

\[\begin{align}
& \quad \ c^{c^x} &\pmod p \\
& \equiv c^{{c^x} \bmod \varphi(p) + \varphi(p)} &\pmod p \\
& \equiv c^{{c^{x \bmod \varphi(\varphi(p)) + \varphi(\varphi(p)) }} \mod \varphi(p) + \varphi(p)} &\pmod p
\end{align}
\]

我们发现多次操作后 \(\varphi(...\varphi(p)) = 1\) 时,最高层就 \(\bmod\) 变成 \(0\) ,然后结果以后就不会改变了。

这个次数约是 \(O(\log p)\) 次的,我们就有个很直观的想法。

考虑预处理每个数进行多次操作后变成的数,然后每次暴力改需要改的数。

然后对于线段树每个区间,维护这个区间中的数改变次数的最小值,如果最小值 \(\ge\) 当前的 \(\varphi\) 的层数,直接退出即可。


至于预处理十分的麻烦,不仅快速幂需要考虑是否 \(\ge \varphi(p)\) ,而且每次乘法都需要考虑这个qwq

然后为了降低复杂度,我们在预处理的时候,对于底数 \(c\) 求一个幂次对于 \(p\) 模的值,可以利用大步小步预处理也就是 \(\sqrt p\) 打个表,然后最后复杂度就可以做到 \(O(\sqrt p \log^2 p + n (\log n + \log p)) \log p)\) 了。

总结

对于不断开方,或者与幂次有关的题,考虑欧拉扩展定理就行啦 qwq 因为是不超过 \(O(\log p)\) 层的。

代码

具体看看代码实现qwq

#include <bits/stdc++.h>

#define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i)
#define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i)
#define Set(a, v) memset(a, v, sizeof(a))
#define Cpy(a, b) memcpy(a, b, sizeof(a))
#define debug(x) cout << #x << ": " << (x) << endl
#define DEBUG(...) fprintf(stderr, __VA_ARGS__) using namespace std; typedef long long ll; template<typename T> inline bool chkmin(T &a, T b) {return b < a ? a = b, 1 : 0;}
template<typename T> inline bool chkmax(T &a, T b) {return b > a ? a = b, 1 : 0;} inline int read() {
int x(0), sgn(1); char ch(getchar());
for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1;
for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48);
return x * sgn;
} void File() {
#ifdef zjp_shadow
freopen ("2142.in", "r", stdin);
freopen ("2142.out", "w", stdout);
#endif
} const int N = 5e4 + 1e3; inline int phi(int x) {
int res = x;
For (i, 2, sqrt(x + .5)) if (!(x % i)) {
while (!(x % i)) x /= i;
res = res / i * (i - 1);
}
if (x > 1) res = res / x * (x - 1);
return res;
} inline int fpm(int x, int power, int mod) {
int res = 1;
for (; power; power >>= 1, x = 1ll * x * x % mod + (1ll * x * x >= mod) * mod)
if (power & 1) res = 1ll * res * x % mod + (1ll * res * x >= mod) * mod;
return res;
} int n, m, Mod, c; int a[N], Phi[33], val[N][33], lim = 0; const int Block = 1 << 14, All = Block - 1;
int Pow1[N][33], Pow2[N][33]; void Math_Init() {
for (Phi[0] = Mod; Phi[lim] > 1; ++ lim)
Phi[lim + 1] = phi(Phi[lim]);
Phi[++ lim] = 1; For (j, 1, lim) {
int cur = fpm(c, Block, Phi[j]);
Pow1[0][j] = Pow2[0][j] = 1;
For (i, 1, Block - 1) {
Pow1[i][j] = 1ll * Pow1[i - 1][j] * cur % Phi[j] + (1ll * Pow1[i - 1][j] * cur >= Phi[j]) * Phi[j];
Pow2[i][j] = 1ll * Pow2[i - 1][j] * c % Phi[j] + (1ll * Pow2[i - 1][j] * c >= Phi[j]) * Phi[j];
}
} For (i, 1, n) {
val[i][0] = a[i];
For (j, 1, lim) {
int cur = a[i] % Phi[j] + (a[i] >= Phi[j]) * Phi[j];
Fordown (k, j - 1, 1) {
cur = 1ll * Pow1[cur / Block][k] * Pow2[cur & All][k] % Phi[k]
+ (1ll * Pow1[cur / Block][k] * Pow2[cur & All][k] >= Phi[k]) * Phi[k];
}
val[i][j] = fpm(c, cur, Mod) % Mod;
}
}
} inline int Plus(int a, int b) {
return (a += b) >= Mod ? a - Mod : a;
} #define lson o << 1, l, mid
#define rson o << 1 | 1, mid + 1, r template<int Maxn>
struct Segment_Tree { int times[Maxn], sumv[Maxn]; inline void Push_Up(int o) {
sumv[o] = Plus(sumv[o << 1], sumv[o << 1 | 1]);
times[o] = min(times[o << 1], times[o << 1 | 1]);
} void Build(int o, int l, int r) {
if (l == r) { sumv[o] = a[l] % Mod; return ; }
int mid = (l + r) >> 1;
Build(lson); Build(rson); Push_Up(o);
} void Update(int o, int l, int r, int ul, int ur) {
if (times[o] >= lim) return ;
if (ul <= l && r <= ur) ++ times[o];
if (l == r) { sumv[o] = val[l][times[o]]; return ; }
int mid = (l + r) >> 1;
if (ul <= mid) Update(lson, ul, ur);
if (ur > mid) Update(rson, ul, ur); Push_Up(o);
} int Query(int o, int l, int r, int ql, int qr) {
if (ql <= l && r <= qr) return sumv[o];
int mid = (l + r) >> 1;
if (qr <= mid) return Query(lson, ql, qr);
if (ql > mid) return Query(rson, ql, qr);
return Plus(Query(lson, ql, qr), Query(rson, ql, qr));
} }; Segment_Tree<N << 2> T; int main () { File(); n = read(); m = read(); Mod = read(); c = read(); For (i, 1, n) a[i] = read(); Math_Init(); T.Build(1, 1, n); For (i, 1, m) {
int opt = read(), l = read(), r = read();
if (!opt)
T.Update(1, 1, n, l, r);
else
printf ("%d\n", T.Query(1, 1, n, l, r));
} return 0; }

LOJ #2142. 「SHOI2017」相逢是问候(欧拉函数 + 线段树)的更多相关文章

  1. LOJ 3094 「BJOI2019」删数——角标偏移的线段树

    题目:https://loj.ac/problem/3094 弱化版是 AGC017C . 用线段树维护那个题里的序列即可. 对应关系大概是: 真实值的范围是 [ 1-m , n+m ] :考虑设偏移 ...

  2. bzoj4869: [Shoi2017]相逢是问候(欧拉函数+线段树)

    这题是六省联考的...据说数据还出了点锅,心疼六省选手QAQ 首先要知道扩展欧拉定理... 可以发现每次区间操作都会使模数进行一次phi操作,而一个数最多取logp次phi就会变成1,这时后面的指数就 ...

  3. [LNOI] 相逢是问候 || 扩展欧拉函数+线段树

    原题为2017六省联考的D1T3 给出一个序列,m次操作,模数p和参数c 操作分为两种: 1.将[l,r]区间内的每个数x变为\(c^x\) 2.求[l,r]区间内数的和%p 首先,我们要了解一些数论 ...

  4. loj #2143. 「SHOI2017」组合数问题

    #2143. 「SHOI2017」组合数问题   题目描述 组合数 Cnm\mathrm{C}_n^mC​n​m​​ 表示的是从 nnn 个互不相同的物品中选出 mmm 个物品的方案数.举个例子, 从 ...

  5. SHOI 2017 相逢是问候(扩展欧拉定理+线段树)

    题意 https://loj.ac/problem/2142 思路 一个数如果要作为指数,那么它不能直接对模数取模,这是常识: 诸如 \(c^{c^{c^{c..}}}\) 的函数递增飞快,不是高精度 ...

  6. LOJ #2135. 「ZJOI2015」幻想乡战略游戏(点分树)

    题意 给你一颗 \(n\) 个点的树,每个点的度数不超过 \(20\) ,有 \(q\) 次修改点权的操作. 需要动态维护带权重心,也就是找到一个点 \(v\) 使得 \(\displaystyle ...

  7. 【LibreOJ】#6396. 「THUPC2018」弗雷兹的玩具商店 / Toyshop 线段树+完全背包

    [题目]#6396. 「THUPC2018」弗雷兹的玩具商店 / Toyshop [题意]给定一个长度为n的物品序列,每个物品有价值.不超过m的重量.要求支持以下三种操作:1.物品价值区间加减,2.物 ...

  8. LOJ #2145. 「SHOI2017」分手是祝愿

    题目链接 LOJ #2145 题解 一道画风正常的--期望DP? 首先考虑如何以最小步数熄灭所有灯:贪心地从大到小枚举灯,如果它亮着则修改它.可以求出总的最小步数,设为\(cnt\). 然后开始期望D ...

  9. LOJ #2141. 「SHOI2017」期末考试

    题目链接 LOJ #2141 题解 据说这道题可以三分(甚至二分)? 反正我是枚举的 = = 先将t和b数组排序后计算出前缀和, 然后枚举最晚的出成绩时间,每次可以O(1)直接计算调整到该时间所需的代 ...

随机推荐

  1. (FZU 2150) Fire Game (bfs)

    题目链接:http://acm.fzu.edu.cn/problem.php?pid=2150 Problem Description Fat brother and Maze are playing ...

  2. Spring Boot 中使用 @Transactional 注解配置事务管理

    事务管理是应用系统开发中必不可少的一部分.Spring 为事务管理提供了丰富的功能支持.Spring 事务管理分为编程式和声明式的两种方式.编程式事务指的是通过编码方式实现事务:声明式事务基于 AOP ...

  3. redis 的使用,及如何使用redis维护数亿人的登录状态

    一.redis中几个常用的方法 redis的使用场景移步本文 select db redis 下默认有有16个表,0~15可以通过:select 2 或者 select 11这样的方式切换表 keys ...

  4. java程序员一些初中级面试题(数据库部分)

    说出一些数据库优化方面的经验? 1.从JDBC编程的角度讲,用PreparedStatement一般来说比Statement性能高,因为在使用时,SQL语句被预编译并存储在PreparedStatem ...

  5. 1 CHM 中文都是乱码

    CHM格式是Windows系统里常见的帮助文档格式,但有时一些CHM格式的文档会局部显示乱码,特别是一些外文文档在中文版Windows里.这是因为,CHM格式文档在Windows下默认是使用IE浏览器 ...

  6. longquan

    /** * 登录后将数据填写到主数据 */ public void login(String login_nr) { //File f = new File(android.os.Environmen ...

  7. ArcGIS 中UniqueValueRenderer和SimpleRenderer的异同点

    唯一值渲染器:UniqueValueRenderer用符号表示一组有匹配属性的图形,这通常用于名词或字符串数据.SimpleRenderer是使用单一符号进行着色分类,不涉及对要素的数据进行处理.这种 ...

  8. JavaSE从入门到精通

      1.JavaSE的安装 windows下安装完成后,配置环境变量如下: JAVA_HOME       C:\Program Files (x86)\Java\jdk1.8.0_91 CLASSP ...

  9. 微信小程序wxml無法實現頁面跳轉的問題

    wxml的 navigator的url設置后無法跳轉? 檢查要跳轉的頁面是否是在APP.json的tabBar里註冊過,如果是tabBar頁面是不能用wx.navigateTo和wx.Redirect ...

  10. pooling的几种形式(转)

    转载地址:http://blog.csdn.net/malefactor/article/details/51078135    原作者:张俊林 CNN是目前自然语言处理中和RNN并驾齐驱的两种最常见 ...