noip模拟赛 Nephren Ruq Insania
题目背景
大样例下发链接: https://pan.baidu.com/s/1nuVpRS1 密码: sfxg
注意:本题大样例4的输出文件修改为 https://pan.baidu.com/s/1bUWuZW
奈芙莲·卢可·印萨尼亚(Nephren-Ruq-Insania)
同为妖精仓库的成体妖精兵,天赋不如珂朵莉一般,只是一个平凡的妖精.
睡觉时如同毯子一般在威廉身上为其保暖。习惯于粘着威廉,在梦境中与艾尔梅莉亚交谈时,自称就像是威廉的宠物一样。
本题题面中含有大量的剧透,建议做题之前将这部番剧看完(
题目描述
她只是一个非常普通的黄金妖精。
在援救打捞队的作战中,他们不幸与(几乎是所有的)第六兽相遇了。
此时的珂朵莉因为接触到星神艾露可本体,正处于昏迷之中。而威廉也无法离开珂朵莉。
默默守护在房间外的她,提起圣剑,走向了战场。
作为本身天赋只是一般的妖精少女,她难以对抗无数倍于自己的六号兽。
没有多久,她开始体力不支。
终于,在源源不断的六号兽面前,她难以抵挡了……
终于,由于魔力过度激发,她已经处在了魔力失控的边缘……
”威廉,拯救,是我们黄金妖精的使命。“
”况且,威廉之前已经救过我们了。“
”所以,已经没有问题了。“
威廉想要救下奈芙莲,但是他自己也已经处于崩溃的边缘。
冥冥之中他想起了曾经学习过的一种魔法。在这最后一刻,或许已经是唯一的办法了。
这种魔法操作的对象是一个咒语组成的序列,每一个单独的咒语拥有自己的法力值。
威廉需要不断地按照之前的记忆,对某一段区间的法力值加上一个数,或者求出某一段区间的法咒共鸣。
分析:这道题部分分还是比较多的.第一个数据点看起来数据非常小,但是3^3^3^3^3mod p会算不出来,因为次数很大,不能直接对次数取模.怎么将次数变小呢?欧拉定理! ,可以发现如果这道题就是不断地使用欧拉定理,直到φ(p)变成1或者计算完整个区间,这实际上就是一个递归的过程.用线段树进行区间修改,单点查询。
有几个地方需要注意:
1.如果区间[l,r]中第x位是1,那么[x,r]都不需要考虑了,因为1^n = 1.
2.欧拉定理成立的条件是x >= φ(p),而由于忽略0和1的情况,2^2^2^2^2就足以满足数据范围了,所以暴力枚举5位乘起来看看是不是>=φ(p)就可以了.如果<φ(p)就可以直接算,而不需要欧拉定理了.
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; const int maxn = 500010; int n, m, prime[20000010], phi[20000010], cnt, flag[maxn]; long long tag[maxn << 2], c[maxn << 2], a[maxn], L[maxn << 2], R[maxn << 2]; bool vis[20000010]; void init() { phi[1] = 1; for (int i = 2; i <= 20000000; i++) { if (!vis[i]) { prime[++cnt] = i; phi[i] = i - 1; } for (int j = 1; j <= cnt; j++) { int t = i * prime[j]; if (t > 20000000) break; vis[t] = 1; if (i % prime[j] == 0) { phi[t] = phi[i] * prime[j]; break; } phi[t] = phi[i] * (prime[j] - 1); } } } void pushup(int o) { c[o] = c[o * 2] + c[o * 2 + 1]; } void pushdown(int o) { if (tag[o]) { tag[o * 2] += tag[o]; tag[o * 2 + 1] += tag[o]; c[o * 2] += (R[o * 2] - L[o * 2] + 1) * tag[o]; c[o * 2 + 1] += (R[o * 2 + 1] - L[o * 2 + 1] + 1) * tag[o]; } tag[o] = 0; } void build(int o, int l, int r) { L[o] = l; R[o] = r; if (l == r) { c[o] = a[l]; return; } int mid = (l + r) >> 1; build(o * 2, l, mid); build(o * 2 + 1, mid + 1, r); pushup(o); } void update(int o, int l, int r, int x, int y, int v) { if (x <= l && r <= y) { tag[o] += v; c[o] += v; return; } pushdown(o); int mid = (l + r) >> 1; if (x <= mid) update(o * 2, l, mid, x, y, v); if (y > mid) update(o * 2 + 1, mid + 1, r, x, y, v); pushup(o); } long long query(int o, int l, int r, int pos) { if (l == r) return c[o]; pushdown(o); int mid = (l + r) >> 1; if (pos <= mid) return query(o * 2, l, mid, pos); else return query(o * 2 + 1, mid + 1, r, pos); } long long Cal(int q) { if (flag[q] == m) return a[q]; flag[q] = m; return a[q] = query(1, 1, n, q); } long long qpow(long long a, long long b, long long mod) { a %= mod; long long res = 1; while (b) { if (b & 1) res = (res * a) % mod; a = (a * a) % mod; b >>= 1; } return res; } long long jisuan(int l, int r, int mod) { if (mod == 1) return 1; if (l == r) { long long t = Cal(l); if (t < mod) //直接算 return t % mod; else return (t % mod) + mod; } int minn = min(n, l + 5); for (int i = l + 1; i <= minn; i++) if (Cal(i) == 1) { minn = i; break; } long long p = Cal(minn), tot = 0; for (int i = minn - 1; i >= l + 1; i--) { tot = p; p = 1; while (tot--) { p *= Cal(i); if (p >= phi[mod]) return qpow(Cal(l) % mod, jisuan(l + 1, r, phi[mod]) + phi[mod], mod); } } return qpow(Cal(l) % mod, jisuan(l + 1, r, phi[mod]), mod); } int main() { memset(flag, -1, sizeof(flag)); init(); scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) scanf("%lld", &a[i]); build(1, 1, n); while (m--) { int op, l, r, mod; scanf("%d%d%d%d", &op, &l, &r, &mod); if (op == 1) update(1, 1, n, l, r, mod); else printf("%lld\n", jisuan(l, r, mod) % mod); } return 0; }