@description@
我们称一个序列是 k-d 的,当且仅当我们可以加入最多 k 个数,然后将序列排序,最终得到的序列是等差的且公差为 d。
给定一个序列 a,求 a 中的一个最长子区间,使得该子区间是 k-d 的。
1 ≤ n ≤ 2·10^5; 0 ≤ k ≤ 2·10^5; 0 ≤ d ≤ 10^9; -10^9 ≤ ai ≤ 10^9。
@solution@
先特判 d = 0 的情况:找最长连续相同的段。
假如一个区间能够加入若干数使其满足条件,则这个区间内的所有数 mod d 应相等。其次,这个区间还应满足任意两个数互不相等。
可以求出每一个点 i 作为右端点,满足以上两个条件的左端点最远到 le[i]。
对于满足以上两个条件的区间,我们尝试去求所需要加入的数最少为多少。
求出该区间 [l, r] 的 max 与 min,则 (max - min) / d 为它的最少项数。现在已经有 r - l + 1 项,则最少需要 (max - min) / d - (r - l + 1) 项。
注意 (max - min) / d 可以写成 (max - max mod d) / d - (min - min mod d) / d。
扫描右端点,通过单调队列可以 O(n) 修改得到每个左端点对应的 max 与 min,在线段树上存储一下每个左端点 (max - max mod d) / d - (min - min mod d) / d + l - 1 的值。
查询时只需要在 [le[i], i] 中线段树二分找最左边的 <= r 的值。
时间复杂度 O(nlogn)。
@accepted code@
#include <map> #include <stack> #include <cstdio> #include <iostream> #include <algorithm> using namespace std; #define mp make_pair #define fi first #define se second typedef pair<int, int> pii; typedef long long ll; const int MAXN = 200000; const ll INF = ll(1E15); struct segtree{ #define lch (x<<1) #define rch (x<<1|1) struct node{ int le, ri; ll mn, tg; }t[4*MAXN + 5]; void pushup(int x) { t[x].mn = min(t[x<<1].mn, t[x<<1|1].mn); } void build(int x, int l, int r) { t[x].le = l, t[x].ri = r, t[x].tg = 0; if( l == r ) { t[x].mn = l; return ; } int m = (l + r) >> 1; build(lch, l, m), build(rch, m + 1, r); pushup(x); } void update(int x, ll k) { t[x].mn += k, t[x].tg += k; } void pushdown(int x) { if( t[x].tg ) { update(lch, t[x].tg), update(rch, t[x].tg); t[x].tg = 0; } } void modify(int x, int l, int r, ll d) { if( l > t[x].ri || t[x].le > r ) return ; if( l <= t[x].le && t[x].ri <= r ) { update(x, d); return ; } pushdown(x); modify(lch, l, r, d); modify(rch, l, r, d); pushup(x); } int query(int x, int k) { if( t[x].le == t[x].ri ) return t[x].le; pushdown(x); if( t[lch].mn <= k ) return query(lch, k); else return query(rch, k); } }T; map<int, int>lst; int a[MAXN + 5], b[MAXN + 5], r[MAXN + 5]; stack<pii>s1, s2; int main() { int n, k, d; scanf("%d%d%d", &n, &k, &d); for(int i=1;i<=n;i++) scanf("%d", &a[i]); if( d == 0 ) { int ansl = 1, ansr = 1, le = 1; for(int i=2;i<=n;i++) { if( a[i] != a[le] ) le = i; if( i - le > ansr - ansl ) ansl = le, ansr = i; } printf("%d %d\n", ansl, ansr); } else { for(int i=1;i<=n;i++) r[i] = (a[i] % d + d) % d, b[i] = (a[i] - r[i]) / d; int ansl = 1, ansr = 1, le = 1; T.build(1, 1, n), T.update(1, -k); s1.push(mp(1, 1)), s2.push(mp(1, 1)); lst[a[1]] = 1; for(int i=2;i<=n;i++) { pii p = mp(i, i); while( !s1.empty() && b[s1.top().se] < b[i] ) { p = mp(s1.top().fi, i); T.modify(1, s1.top().fi, s1.top().se, -b[s1.top().se]), s1.pop(); } T.modify(1, p.fi, p.se, b[p.se]), s1.push(p); p = mp(i, i); while( !s2.empty() && b[s2.top().se] > b[i] ) { p = mp(s2.top().fi, i); T.modify(1, s2.top().fi, s2.top().se, b[s2.top().se]), s2.pop(); } T.modify(1, p.fi, p.se, -b[p.se]), s2.push(p); if( r[i] != r[i-1] ) T.modify(1, le, i - 1, INF), le = i; int x = lst[a[i]] + 1; if( le < x ) T.modify(1, le, x - 1, INF), le = x; lst[a[i]] = i; int j = T.query(1, i); if( i - j > ansr - ansl ) ansl = j, ansr = i; } printf("%d %d\n", ansl, ansr); } } /* r - l + 1 + k >= max - min + 1 r >= max - min + l - k */
@details@
感觉难度系数评到 3100 是不是太高了。。。毕竟我都会做的题。
来源:https://www.cnblogs.com/Tiw-Air-OAO/p/12020571.html