LOJ #2142. 「SHOI2017」相逢是问候(欧拉函数 + 线段树)

题意

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

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

n,m5×104,p214

题解

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

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

(1) ccx(modp)(2)ccxmodφ(p)+φ(p)(modp)(3)ccxmodφ(φ(p))+φ(φ(p))modφ(p)+φ(p)(modp)

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

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

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

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


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

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

总结

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

代码

具体看看代码实现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; }

__EOF__

本文作者zjp_shadow
本文链接https://www.cnblogs.com/zjp-shadow/p/9757406.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zjp_shadow  阅读(386)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】
点击右上角即可分享
微信分享提示