[洛谷P4231]三步必殺 題解
洛谷P4231 三步必殺
- 思路:
題目中資料範圍為 \(N=10^7\) ,這很明顯是在告訴我們正解是 \(\operatorname{O}(N)\) 演算法。
而能做到 \(\operatorname{O}(1)\) 靜態修改與查詢的自然就是差分了。
設原陣列的 \(A\) ,差分陣列為 \(B\) ,進行操作 \((l,r,s,t)\) 時,令公差為 \(d = \frac{t - s}{r - l}\),則
\(\forall i \in [l,r],A_i \gets A_i + s + (i-l) \times d\)
手推一下對應的差分陣列 \(B\) 的變化:
\(B_l \gets B_l + s\)
\(\forall i \in (l,r],B_i \gets B_i + d\)
\(B_{r + 1} \gets B_{r + 1} - t\)
發現 \(B\) 陣列中仍然要進行區間修改操作,而差分陣列只能支援單點修改,怎麼辦呢?
那就再增加一個 \(B\) 的差分陣列 \(C\),推一下 \(B\) 陣列變化時 \(C\) 陣列的變化:
對 \(B_l\) 執行單點修改,則在 \(C\) 中:\(C_l \gets C_l+s\) , \(C_{l+1} \gets C_{l+1} - s\)
對 \((l,r]\) 執行區間修改,則:\(C_{l+1} \gets C_{l+1} + d\)
對 \(B_{r+1}\) 執行單點修改,則: \(C_{r + 1} \gets C_{r + 1} - t\) , \(C_{r + 2} \gets C_{r+2} + t\)
那麼在程式中我們只需要開一個數組 \(C\) ,操作中按照上述方法更新。
統計時開兩個字首和變數 \(sum_1,sum_2\),對應的算出 \(B\) 陣列的元素,\(A\) 陣列的元素,最後統計一下答案即可。
code:
#include <cstdio> #include <cstring> #include <algorithm> #include <cctype> using namespace std; const int maxn = 1e7 + 5; int read() { int s = 0; char c = getchar(); for(;!isdigit(c);c = getchar()); for(;isdigit(c);c = getchar())s = (s << 1) + (s << 3) + (c ^ '0'); return s; } int n,m; long long c[maxn]; int main() { n = read(); m = read(); for(int i = 1;i <= m;++ i) { int l = read(),r = read(),s = read(),e = read(); int d = (e - s) / (r - l); c[l] += (long long)s; c[l + 1] -= (long long)s; c[l + 1] += (long long)d; c[r + 1] -= (long long)d; c[r + 1] -= (long long)e; c[r + 2] += (long long)e; } long long sum1 = 0,sum2 = 0,ans = 0,cnt = 0; for(int i = 1;i <= n;++ i) { sum1 += c[i]; sum2 += sum1; cnt ^= sum2; ans = max(ans , sum2); } printf("%lld %lld",cnt,ans); return 0; }