题意:求最长区间使得加入 \(k\) 个数后 sort 后最后为一个公差为 \(d\) 的等差数列。多解输出 \(l\) 最小的答案。
\(n,k\leq 2\times 10^5,0\leq d\leq 10^9\)
-
\(d=0\) 的情况特判一下,找最长的值都相同的段即可
-
\(d\neq 0\) 的情况:
是公差为 \(d\) 等差数列需要满足两个条件:
- \(\bmod\ d\) 相同;
- 值两两不同。
可以单独处理每个极长的同余的段,然后分开处理:
先都整除一下 \(d\),问题都转化成处理公差 \(d\) 为 \(1\) 的情况。
这个区间合法要求 \(\max-\min+1-(R-L+1)\leq k\),枚举 \(r\) 统计最小的 \(l\) 使得:
-
\([L,R]\) 内元素无重复;
-
\(\max-\min+1-(R-L+1)\leq k\)。
其中 \(\max\)/\(\min\) 为区间 \([L,R]\) 的最大/小值
无重复的条件很好做,记录一下每个值上次出现的位置,然后记录一下 \(l\) 的下界 \(T\)。
设 \(w_L=\max(L,R)-\min(L,R)+L\)
求 \([T+1,R]\) 中最左的 \(L\) 使得 \(w_L\leq k+R\),假如我们可以快速维护 \(w\) ,就可以解决这个问题。
怎样维护 \(w\)?
注意 \(\max(L,R)\) 是递减的,\(\min\) 也类似。\(\max(L,R)\) 可以维护一个单调栈,栈中的每个点都代表一段区间,它们的 \(\max(L,R)\) 是这个栈中的这个元素。那么每次弹出的时候修改这个区间的 \(w\) 即可。
操作是区间加减,需要支持的查询为区间最左侧小于等于某个数的位置,这是个经典问题,记录一下区间最小值即可。
均摊下来复杂度是对的,操作数仅有 \(\mathcal{O}(n)\) 个。
实现上有几个小细节,对于每个同余段重新建一棵大小为这个段长度的树,如果每次都 \(\mathcal{O}(n)\) 建树,复杂度是错误的。\(a_i\) 有负数,提前全加上 \(10^9\) 转化成非负数,不影响答案。
综上所述,我们完美的解决了这个问题,时间复杂度 \(O(n\log n)\)
//Code by do_while_true
#include<iostream>
#include<cstdio>
#include<map>
template <typename T> T Max(T x, T y) { return x > y ? x : y; }
template <typename T> T Min(T x, T y) { return x < y ? x : y; }
template <typename T>
T &read(T &r) {
r = 0; bool w = 0; char ch = getchar();
while(ch < '0' || ch > '9') w = ch == '-' ? 1 : 0, ch = getchar();
while(ch >= '0' && ch <= '9') r = (r << 3) + (r <<1) + (ch ^ 48), ch = getchar();
return r = w ? -r : r;
}
const int N = 200010;
const int INF = 0x7fffffff;
int n, k, d;
int a[N];
std::map<int, int>pre;
#define tl tree[x].l
#define tr tree[x].r
#define ls tree[x].lson
#define rs tree[x].rson
int trnt;
struct SGT {
int l, r, sum, mn, tag, lson, rson;
}tree[N << 1];
inline void pushup(int x) { tree[x].mn = Min(tree[ls].mn, tree[rs].mn); }
inline void pushdown(int x) {
if(tree[x].tag) {
int &p = tree[x].tag;
tree[ls].mn += p; tree[rs].mn += p;
tree[ls].tag += p; tree[rs].tag += p;
p = 0;
}
}
int build(int l, int r) {
int x = ++trnt; tl = l; tr = r;
tree[x].mn = tree[x].tag = tree[x].lson = tree[x].rson = 0;
if(l == r) return x;
ls = build(l, (l + r) >> 1); rs = build(tree[ls].r+1, r);
return x;
}
void modify(int x, int l, int r, int v) {
if(tree[x].l >= l && tree[x].r <= r) {
tree[x].mn += v;
tree[x].tag += v;
return ;
}
pushdown(x);
int mid = (tr + tl) >> 1;
if(mid >= l) modify(ls, l, r, v);
if(mid < r) modify(rs, l, r, v);
pushup(x);
}
int query(int x, int l, int r, int v) {
if(tree[x].mn > v) return INF;
if(tl >= l && tr <= r) {
if(tl == tr) return tl;
pushdown(x); int sumq = 0;
if(tree[ls].mn <= v) sumq = query(ls, l, r, v);
else sumq = query(rs, l, r, v);
pushup(x);
return sumq;
}
pushdown(x);
int mid = (tl + tr) >> 1, sumq;
if(r <= mid) sumq = query(ls, l, r, v);
else if(l > mid) sumq = query(rs, l, r, v);
else {
sumq = query(ls, l, r, v);
if(sumq == INF) sumq = query(rs, l, r, v);
}
pushup(x);
return sumq;
}
#undef tl
#undef tr
#undef ls
#undef rs
int L[N], R[N], bcnt, ansl = 1, ansr = 1;
int st1p[N], st1x[N], top1;
int st2p[N], st2x[N], top2;
int down;
signed main() {
// freopen("in.txt", "r", stdin);
read(n); read(k); read(d);
if(!d) {
for(int i = 1; i <= n; ++i) read(a[i]);
int l = 1;
for(int i = 2; i <= n; ++i) {
if(a[i] != a[i-1]) {
if(i - l > ansr - ansl + 1) ansl = l, ansr = i-1;
l = i;
}
}
printf("%d %d\n", ansl, ansr);
return 0;
}
for(int i = 1; i <= n; ++i) read(a[i]), a[i] += 1000000000;
L[bcnt = 1] = 1;
for(int i = 2; i <= n; ++i) {
if(a[i] % d != a[i-1] % d) {
R[bcnt] = i-1;
L[++bcnt] = i;
}
}
R[bcnt] = n;
for(int i = 1; i <= bcnt; ++i) {
if(R[i] - L[i] == 0) continue ;
int nowl = 1, nowr = 0;
pre.clear();
top1 = top2 = 0; trnt = 0; down = 1;
build(1, R[i] - L[i] + 1);
for(int j = L[i]; j <= R[i]; ++j) a[j] /= d;
for(int j = L[i]; j <= R[i]; ++j) {
if(pre[a[j]]) down = Max(down, pre[a[j]]-L[i]+2);
pre[a[j]] = j;
while(top1 && st1x[top1] <= a[j]) modify(1, st1p[top1-1]+1, st1p[top1], -st1x[top1]), --top1;
while(top2 && st2x[top2] >= a[j]) modify(1, st2p[top2-1]+1, st2p[top2], st2x[top2]), --top2;
st1x[++top1] = a[j]; st1p[top1] = j-L[i]+1;
st2x[++top2] = a[j]; st2p[top2] = j-L[i]+1;
modify(1, st1p[top1-1]+1, st1p[top1], st1x[top1]);
modify(1, st2p[top2-1]+1, st2p[top2], -st2x[top2]);
modify(1, j-L[i]+1, j-L[i]+1, j);
int pl = query(1, down, j-L[i]+1, k+j);
if(pl != INF && j - (pl+L[i]-1) + 1 > nowr - nowl + 1) nowl = pl+L[i]-1, nowr = j;
}
if(nowr - nowl + 1 > ansr - ansl + 1) ansl = nowl, ansr = nowr;
}
printf("%d %d\n", ansl, ansr);
return 0;
}