JZOJ 5451.Genocide

题目

题解

对于 (m=1) 这档分
我们可以 (dp) 然后斜率优化
具体来说就是 (f_i = f_j + frac{(i-j) imes (i-j+1)}{2} + sum[j]-sum[i])
很容易斜率优化

那么 (m=3 imes 10^5)
考虑 (cdq) 分治
考虑 (dp) 出一个如上定义的前缀 (f) 和同理的后缀 (g)
(h_x) 表示强制选 (x) 时的最大收益,(h_x = f_i+g_j-sum_{j-1}+sum_i+frac{(j-i) imes(j-i-1)}{2}(i < x <j))
这个 (O(n^2)) 显然不行
(F_i = f_i+frac{i imes (i-1)}{2}+sum_i,G_i = g_i + frac{i imes (i-1)}{2}-sum_{i-1})
(h_x = F_i + G_j - i imes j(i < x < j))
对于区间 ([l,r]),我们讨论 ([l,mid])(h)
对于一个 (lle xle mid),它的 (h)(i) 决策在它左边,(j) 决策 (mid) 右边
我们考虑对 (j) 决策斜率优化,(i) 递增,和 (f,g) 的算法有异曲同工之妙

然后通过一些玄学操作同理处理决策 (i)
最后答案就是 (max(f_{p-1}+g_{p+1},h_p+a_p-x))

(Code)

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#define LL long long
using namespace std;

const int N = 3e5 + 5;
const LL INF = 0x3f3f3f3f3f3f3f3f;
int n, m, a[N], Q[N];
LL sum[N], f[N], g[N], F[N], G[N], h[N], w[N];

inline double slope(LL *f, int j, int k)
{
	return 1.0 * ((f[j] + (1LL * j * j - j) / 2.0 + sum[j]) - (f[k] + (1LL * k * k - k) / 2.0 + sum[k])) / (j - k);
}
inline void dp(LL *f)
{
	memset(f, 0, sizeof f);
	int top;
	Q[top = 1] = 0;
	for(register int i = 1; i <= n; i++)
	{
		while (top > 1 && slope(f, Q[top - 1], Q[top]) < i) --top;
		f[i] = max(f[i - 1], f[Q[top]] + 1LL * (i - Q[top]) * (i - Q[top] + 1) / 2 - sum[i] + sum[Q[top]]);
		while (top && slope(f, Q[top - 1], Q[top]) < slope(f, Q[top], i)) --top;
		Q[++top] = i;
	}
}

inline double slope(int j, int k){return 1.0 * (G[j] - G[k]) / (j - k);}
void divide(int l, int r, int p)
{
	int mid = (l + r) >> 1, top = 0;
	for(register int i = mid + 1; i <= r; i++)
	{
		while (top > 1 && slope(Q[top - 1], Q[top]) < slope(Q[top], i)) --top;
		Q[++top] = i;
	}
	w[l - 1] = -INF;
	for(register int i = l; i <= mid; i++)
	{
		while (top > 1 && slope(Q[top - 1], Q[top]) < i) --top;
		w[i] = max(w[i - 1], F[i] + G[Q[top]] - 1LL * i * Q[top]);
	}
	if (p) for(register int i = l; i <= mid; i++) h[n - i + 1] = max(h[n - i + 1], w[i - 1]);
	else for(register int i = l; i <= mid; i++) h[i] = max(h[i], w[i - 1]);
	if (l ^ r) divide(l, mid, p), divide(mid + 1, r, p);
}

void solve()
{
	for(register int i = 1; i <= n; i++) sum[i] = sum[i - 1] + a[i];
	dp(f);
	reverse(a + 1, a + n + 1);
	for(register int i = 1; i <= n; i++) sum[i] = sum[i - 1] + a[i];
	dp(g);
	
	reverse(f + 1, f + n + 1);
	for(register int i = 1; i <= n; i++) swap(f[i], g[i]);
	for(register int i = 0; i <= n + 1; i++) F[i] = f[i] + 1LL * i * (i + 1) / 2 + sum[i], G[i] = g[i] + 1LL * i * (i - 1) / 2 - sum[i - 1];
	for(register int i = 1; i <= n; i++) h[i] = -INF;
	divide(0, n + 1, 1);
	
	for(register int i = 1; i <= n; i++) swap(f[i], g[i]);
	reverse(a + 1, a + n + 1), reverse(f + 1, f + n + 1), reverse(g + 1, g + n + 1);
	for(register int i = 1; i <= n; i++) sum[i] = sum[i - 1] + a[i];
	for(register int i = 0; i <= n + 1; i++) F[i] = f[i] + 1LL * i * (i + 1) / 2 + sum[i], G[i] = g[i] + 1LL * i * (i - 1) / 2 - sum[i - 1];
	divide(0, n + 1, 0);
}

int main()
{
	freopen("genocide.in", "r", stdin);
	freopen("genocide.out", "w", stdout);
	scanf("%d", &n);
	for(register int i = 1; i <= n; i++) scanf("%d", &a[i]);
	solve();
	scanf("%d", &m);
	for(int p, x; m; --m)
	{
		scanf("%d%d", &p, &x);
		printf("%lld
", max(f[p - 1] + g[p + 1], h[p] + a[p] - x));
	}
}
原文地址:https://www.cnblogs.com/leiyuanze/p/14316467.html