【JZOJ4665】【CF407E】k-d-sequence

题目链接:k-d-sequence

题目大意

给一个长度为(n)的序列(a),要你找一个最长的区间,使得将区间内的数排序以后,最多加(k)个数使它构成公差为(d)的等差数列。长度相同取左端点最靠左的。
(nleq 200000,kleq 200000,dleq 10^9,|a_i|leq 10^9)

解析

一个小插曲:这题JZOJ上的数据很不靠谱,随意就水过了。CF上的数据强一点。

首先我们来分析一段区间([l,r])怎么样才满足条件:

  • 没有相等的数(区间长度(>1)
  • 所有数模(d)同余(这样才能保证公差为(d)
  • (frac{max_{i=l}^{r} {a_i}-min_{i=l}^{r} {a_i}}{d}+1leq r-l+1+k)

前两个条件保证这个序列能够通过补数成为公差为(d)的等差数列,第三个条件保证要补的数不超过(k)个。

对于前两个条件,我们可以用一个数组maxr[i]表示以(i)为左端点,满足这两个条件的最远右端点,这个值具有单调性,可以(O(n))做出来。

对于第三个条件,我们先把所有的(a_i)除以(d),然后变一下式子,得到:

[max_{i=l}^{r}{a_i}-min_{i=l}^{r}{a_i} - r leq k-l ]

此时式子右边仅跟(l)有关,我们考虑倒序枚举(l),若能用某个数据结构维护每个(r)对应的(b_r=max_{i=l}^{r}{a_i}-min_{i=l}^{r}{a_i} - r),就好做了。

考虑(l)的移动使(b_r)发生的变化,用个单调栈维护(a_l)作为最大(小)值向右最远延伸到的位置(f_l),那么现在(a_l)是老大了,原来的假老大都得消除影响,可以发现一个数作为最大值影响的是一个区间,我们要把一个区间减掉某个数再加上某个数,这不显然用线段树维护吗?

那么问题来了,如何查询?我们要一个最右的端点(r)使得(b_rleq k-l),并且还要满足(lleq rleq maxr[l])

其实维护一个区间最小值就行了,在线段树上判断右边(b_r)的最小值是否(leq k-l),是就往右走。为了防止跳到(l)之前或者(maxr[l])之后去,我们先把这两段加上无穷大,做完再减掉就行了。

注意,单调栈的过程是跟着倒序枚举(l)的过程一起做的。

此外记得特判(d=0)的情况,不然要出锅。

Code

#include <cstdio>
#include <cstring>
#include <algorithm>
#define lson rt << 1
#define rson rt << 1 | 1
using namespace std;

typedef long long ll;
const int N = 200007;
const ll INF = 0x3f3f3f3f;
ll min(ll a, ll b) { return a < b ? a : b; }

int ans = 1, ansl = 1;
int n, k, len, maxr[N], c[N], mo[N];
ll d, a[N], b[N];
int buc[N];

int top[2], stk[N][2];

ll mi[N * 4], inc[N * 4];
void upd(int rt) { mi[rt] = min(mi[lson], mi[rson]); }
void build(int rt, int l, int r)
{
	if (l == r) { mi[rt] = -l; return; }
	int mid = l + r >> 1;
	build(lson, l, mid), build(rson, mid + 1, r), upd(rt);
}
void down(int rt, int l, int r)
{
	if (!inc[rt]) return;
	if (l != r) inc[lson] += inc[rt], inc[rson] += inc[rt];
	mi[rt] += inc[rt], inc[rt] = 0;
}
void modify(int rt, int l, int r, int ql, int qr, ll v)
{
	down(rt, l, r);
	if (ql <= l && r <= qr) { inc[rt] += v; return; }
	int mid = l + r >> 1;
	if (ql <= mid) modify(lson, l, mid, ql, qr, v);
	if (mid + 1 <= qr) modify(rson, mid + 1, r, ql, qr, v);
	down(lson, l, mid), down(rson, mid + 1, r), upd(rt);
}
int getit(int rt, int l, int r, ll v)
{
	down(rt, l, r);
	if (l == r) return mi[rt] <= v ? l : -1;
	int mid = l + r >> 1;
	down(lson, l, mid), down(rson, mid + 1, r);
	if (mi[rson] <= v) return getit(rson, mid + 1, r, v);
	else return getit(lson, l, mid, v);
}

void doit()
{
	for (int i = 1; i <= n; i++)
	{
		int j = i;
		while (j < n && a[j + 1] == a[i]) j++;
		if (j - i + 1 > ans) ans = j - i + 1, ansl = i;
		else if (j - i + 1 == ans && i < ansl) ansl = i;
		i = j;
	}
	printf("%d %d
", ansl, ansl + ans - 1);
}

int main()
{
	scanf("%d%d%I64d", &n, &k, &d); //cf的%I64d
	for (int i = 1; i <= n; i++) scanf("%I64d", &a[i]), a[i] += 1e9;
	if (d == 0) { doit(); return 0; }
	memcpy(b, a, sizeof(a));
	sort(b + 1, b + n + 1);
	len = unique(b + 1, b + n + 1) - b - 1;
	for (int i = 1; i <= n; i++) c[i] = lower_bound(b + 1, b + n + 1, a[i]) - b, mo[i] = a[i] % d;
	buc[c[1]]++;
	for (int i = 1, j = 1; i <= n; i++)
	{
		while (j + 1 <= n && mo[j + 1] == mo[i] && !buc[c[j + 1]]) j++, buc[c[j]]++;
		maxr[i] = j, buc[c[i]]--;
	}
	build(1, 1, n);
	stk[0][0] = stk[0][1] = n + 1;
	for (int i = n; i; i--)
	{
		a[i] /= d;
		if (!top[0]) modify(1, 1, n, i, n, -a[i]);
		if (!top[1]) modify(1, 1, n, i, n, a[i]);
		for (; top[0] && a[i] < a[stk[top[0]][0]]; top[0]--) modify(1, 1, n, stk[top[0]][0], stk[top[0] - 1][0] - 1, a[stk[top[0]][0]] - a[i]);
		for (; top[1] && a[i] > a[stk[top[1]][1]]; top[1]--) modify(1, 1, n, stk[top[1]][1], stk[top[1] - 1][1] - 1, a[i] - a[stk[top[1]][1]]);
		stk[++top[0]][0] = stk[++top[1]][1] = i;
		if (maxr[i] < n) modify(1, 1, n, maxr[i] + 1, n, INF);
		if (i > 1) modify(1, 1, n, 1, i - 1, INF);
		int r = getit(1, 1, n, k - i);
		if (r != -1 && r - i + 1 >= ans) ans = r - i + 1, ansl = i;
		if (maxr[i] < n) modify(1, 1, n, maxr[i] + 1, n, -INF);
		if (i > 1) modify(1, 1, n, 1, i - 1, -INF);
	}
	printf("%d %d
", ansl, ans + ansl - 1);
	return 0;
}
原文地址:https://www.cnblogs.com/zjlcnblogs/p/11134731.html