1. 程式人生 > 其它 >[洛谷P4231]三步必殺 題解

[洛谷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\)

, \(C_{r + 1} \gets C_{r + 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;
}