原题链接
最优清零方案
解题思路
- 使用线段树进行区间修改和区间查询。
- 假设全部进行操作1,需要的操作次数为数列和;每使用一次操作2,相较于全部使用操作1减少的操作次数为k-1;
具体代码
import java.io.*;
public class Main {
public static int N, K, MAX = 1_000_005;
public static int[] arr = new int[MAX];
public static long ans;
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
StreamTokenizer in = new StreamTokenizer(br);
PrintWriter out = new PrintWriter(new OutputStreamWriter(System.out));
ans = 0;
while (in.nextToken() != StreamTokenizer.TT_EOF) {
N = (int) in.nval;
in.nextToken();
K = (int) in.nval;
for (int i = 1; i <= N; i++) {
in.nextToken();
arr[i] = (int) in.nval;
ans += arr[i];
}
}
out.println(solve());
br.close();
out.flush();
out.close();
}
public static long solve() {
build(1, N, 1);
for (int i = 1, j = i + K - 1; j <= N; i++, j++) {
int tempMin = queryMin(i, j, 1, N, 1);
if (tempMin > 0) {
add(i, j, -tempMin, 1, N, 1);
ans -= tempMin * K - tempMin;
}
}
return ans;
}
public static int[] min = new int[MAX << 2];
public static int[] max = new int[MAX << 2];
public static int[] add = new int[MAX << 2];
public static int f(int l, int r, int i) {
if (max[i] == 0) {
return 0;
}
down(i);
if (l == r) {
return max[i];
}
int res = 0;
int mid = (l + r) / 2;
res += f(l, mid, i << 1) + f(mid + 1, r, i << 1 | 1);
return res;
}
public static void build(int l, int r, int i) {
if (l > r)
return;
add[i] = 0;
if (l == r) {
min[i] = arr[l];
max[i] = arr[l];
} else {
int mid = (l + r) / 2;
build(l, mid, i << 1);
build(mid + 1, r, i << 1 | 1);
up(i);
}
}
public static void up(int i) {
min[i] = Math.min(min[i << 1], min[i << 1 | 1]);
max[i] = Math.max(max[i << 1], max[i << 1 | 1]);
}
public static void down(int i) {
if (add[i] != 0) {
lazyAdd(i << 1, add[i]);
lazyAdd(i << 1 | 1, add[i]);
add[i] = 0;
}
}
public static void lazyAdd(int i, int v) {
min[i] += v;
max[i] += v;
add[i] += v;
}
public static int queryMin(int jobl, int jobr, int l, int r, int i) {
if (jobl <= l && r <= jobr) {
return min[i];
}
down(i);
int mid = (l + r) / 2;
int ans = Integer.MAX_VALUE;
if (jobl <= mid) {
ans = Math.min(ans, queryMin(jobl, jobr, l, mid, i << 1));
}
if (mid + 1 <= jobr) {
ans = Math.min(ans, queryMin(jobl, jobr, mid + 1, r, i << 1 | 1));
}
return ans;
}
public static void add(int jobl, int jobr, int jobv, int l, int r, int i) {
if (jobl <= l && r <= jobr) {
lazyAdd(i, jobv);
} else {
down(i);
int mid = (l + r) / 2;
if (jobl <= mid) {
add(jobl, jobr, jobv, l, mid, i << 1);
}
if (jobr > mid) {
add(jobl, jobr, jobv, mid + 1, r, i << 1 | 1);
}
up(i);
}
}
}