1. 程式人生 > 實用技巧 >E. Two Editorials

E. Two Editorials

暴力列舉選的區間右端點是 \(O(n^2m)\) 的,考慮優化,對於一個時間段 \(l, r\) 當選擇講題的區間右端點為 \(x\)
獲得價值為 \(v\),顯然 \(v\) 先隨著 \(x\) 遞增,然後不變,最後在遞減。

考慮計算出 \(f_{i,j}\) 表示選取區間兩個端點為 \(i,j\) 時,i 所產生的貢獻,規定當 \(i < j\) 並且 \(v_i < v_j\) 時,在 \(i\) 出計算,否則在 \(j\) 出計算。

然後對於每個 \(i\)\(v\) 比它小的 \(j\) 是連續的兩端,差分即可。

最後計算 \(f_{i,j} + f_{j,i}\) 的最大值即可。

#include <bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for (int i = (a); i <= (b); i++)
#define per(i, a, b) for (int i = (a); i >= (b); i--)
#define fi first
#define se second
const int N = 2005, inf = 0x3f3f3f3f, mod = 1e9 + 7;
typedef pair <int, int> P;
typedef long long LL;

int n, m, l[N], r[N], k, v[N], f[N][N], ans;

int calc_(int x, int y, int i) {
	return max(0, min(y, r[i]) - max(x, l[i]) + 1);
}

int main() {
	scanf("%d%d%d", &n, &m, &k);
	rep (i, 1, m) {
		scanf("%d%d", &l[i], &r[i]);
		rep (j, k, n)
			v[j] = calc_(j - k + 1, j, i);
		int top = k;
		while (v[top] <= v[top + 1] && top < n)
			top++;
		int pos = n + 1;
		rep (j, k, top) {
			f[j][k] += v[j], f[j][j + 1] -= v[j];
			while (v[pos - 1] < v[j] && pos > top)
				pos--;
			f[j][pos] += v[j], f[j][n + 1] -= v[j];
		}
		pos = k - 1;
		per (j, n, top + 1) {
			f[j][j] += v[j], f[j][n + 1] -= v[j];
			while (v[pos + 1] <= v[j] && pos < top)
				pos++;
			f[j][pos + 1] -= v[j], f[j][k] += v[j]; 
		}
	}
	rep (i, k, n)
		rep (j, k, n)
			f[i][j] += f[i][j - 1];
	rep (i, k, n)
		rep (j, k, n)
			ans = max(ans, f[i][j] + f[j][i]*(i != j));
	printf("%d\n", ans);
}