bzoj4869 [Shoi2017]相逢是問候
阿新 • • 發佈:2018-03-25
RM problems 共有m個 mit info dmi tput 代碼 printf
Submit: 1311 Solved: 470
[Submit][Status][Discuss]
第一行有三個整數n,m,p,c,所有整數含義見問題描述。
接下來一行n個整數,表示a數組的初始值。
接下來m行,每行三個整數,其中第一個整數表示了操作的類型。
如果是0的話,表示這是一個修改操作,操作的參數為l,r。
如果是1的話,表示這是一個詢問操作,操作的參數為l,r。
1 ≤ n ≤ 50000, 1 ≤ m ≤ 50000, 1 ≤ p ≤ 100000000, 0 < c <p, 0 ≤ ai < p
1 2 3 4
0 1 4
1 2 4
0 1 4
1 1 3
0
3
4869: [Shoi2017]相逢是問候
Time Limit: 40 Sec Memory Limit: 512 MBSubmit: 1311 Solved: 470
[Submit][Status][Discuss]
Description
Informatikverbindetdichundmich. 信息將你我連結。B君希望以維護一個長度為n的數組,這個數組的下標為從1到n的正整數。一共有m個操作,可以 分為兩種:0 l r表示將第l個到第r個數(al,al+1,...,ar)中的每一個數ai替換為c^ai,即c的ai次方,其中c是 輸入的一個常數,也就是執行賦值ai=c^ai1 l r求第l個到第r個數的和,也就是輸出:sigma(ai),l<=i<=rai因為 這個結果可能會很大,所以你只需要輸出結果mod p的值即可。Input
Output
對於每個詢問操作,輸出一行,包括一個整數表示答案mod p的值。Sample Input
4 4 7 21 2 3 4
0 1 4
1 2 4
0 1 4
1 1 3
Sample Output
3
HINT
鳴謝多名網友提供正確數據,已重測!
Source
黑吉遼滬冀晉六省聯考&&鳴謝xlk授權本OJ使用權
分析:比較難的一道題.
如果做過bzoj3884,就會想到用歐拉定理來降冪. 和區間開根號一樣,一個數操作有限次數後就會變成一個常數. 在它變成常數以前對它暴力修改即可. 為什麽會變成一個常數呢? p --> phi(p) --> phi(phi(p)) ...... 最後一定會變成1. 不論什麽數mod 1都等於0.
區間開根號可以用分塊來做,那麽這道題能不能用分塊來做呢?顯然是不行的,復雜度太高! 每個數會被暴力修改log次,每次修改需要對log個phi求快速冪,快速冪的復雜度也是log的,也就是說:將一個數修改到底的復雜度是O(log^3n)的,顯然是不能接受的. 既然不能分塊,用線段樹做就好了.
令p通過不斷取phi變成1的次數為cnt. 如果要修改一個區間[l,r],若當前區間的所有元素的最少操作次數≥cnt,這個區間的元素就不需要被修改了. 否則暴力修改.
一開始將每一層的phi給記錄下來(模數). 如果要修改第i個元素,第i個元素已經被修改tot次了,那麽就從第tot + 1層到1層逐層計算答案. 需要註意的是:擴展歐拉定理只能在冪次≥phi的時候才能使用,在快速冪的時候判斷一下就好了.
註意:一定要展開phi(1) = 1那一層. p=3,c=2的話,整個序列只有一個數字,一開始是0,接著不斷進行修改操作和查詢操作。
錯誤的代碼,會返回0->1->2->2->2->.... (p=3,c=2),但是事實上應該是0->1->2->1->1->....
#include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> using namespace std; typedef long long ll; const ll maxn = 50010; ll n,m,P,c,a[maxn],p[maxn],cnt,sum[maxn << 2],minn[maxn << 2]; ll phi(ll x) { ll res = x; for (ll i = 2; i <= sqrt(x); i++) { if (x % i == 0) { while (x % i == 0) x /= i; res = res / i * (i - 1); } } if (x > 1) res = res / x * (x - 1); return res; } void pre() { ll t = P; p[0] = P; while (t != 1) { p[++cnt] = phi(t); t = p[cnt]; } p[++cnt] = 1; } void pushup(ll o) { sum[o] = sum[o * 2] + sum[o * 2 + 1]; sum[o] %= p[0]; minn[o] = min(minn[o * 2],minn[o * 2 + 1]); } void build(ll o,ll l,ll r) { if (l == r) { sum[o] = a[l] % p[0]; return; } ll mid = (l + r) >> 1; build(o * 2,l,mid); build(o * 2 + 1,mid + 1,r); pushup(o); } ll qpow(ll a,ll b,ll pp,bool &flag) { flag = false; ll res = 1; while (b) { if (b & 1) { if (res * a >= pp) flag = true; res = (res * a) % pp; } if (b != 1 && a * a >= pp) flag = true; a = (a * a) % pp; b >>= 1; } return res; } ll calc(ll x,ll ph) { ll res = x; bool flag; if (res >= p[ph]) res = res % p[ph] + p[ph]; while (ph--) { x = res; res = qpow(c,x,p[ph],flag); if (flag) res += p[ph]; } //cout << res << endl; return res % p[0]; } void update(ll o,ll l,ll r,ll x,ll y) { if (minn[o] >= cnt) return; if (l == r) { minn[o]++; sum[o] = calc(a[l],minn[o]); return; } ll mid = (l + r) >> 1; if (x <= mid) update(o * 2,l,mid,x,y); if (y > mid) update(o * 2 + 1,mid + 1,r,x,y); pushup(o); } ll query(ll o,ll l,ll r,ll x,ll y) { if (x <= l && r <= y) return sum[o]; ll mid = (l + r) >> 1,res = 0; if (x <= mid) res += query(o * 2,l,mid,x,y); res %= p[0]; if (y > mid) res += query(o * 2 + 1,mid + 1,r,x,y); res %= p[0]; return res; } int main() { scanf("%lld%lld%lld%lld",&n,&m,&P,&c); for (ll i = 1; i <= n; i++) scanf("%lld",&a[i]); pre(); build(1,1,n); for (ll i = 1; i <= m; i++) { ll opt,l,r; scanf("%lld%lld%lld",&opt,&l,&r); if (opt == 0) update(1,1,n,l,r); else printf("%lld\n",query(1,1,n,l,r)); } return 0; }
bzoj4869 [Shoi2017]相逢是問候