題目背景
大樣例下發鏈接: 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; }