zoukankan      html  css  js  c++  java
  • 【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;
    }
    
  • 相关阅读:
    python学习笔记[3]-邮件的发送
    python学习笔记[2]-join 和 split
    python学习笔记[1]-凯撒密码
    tar 指令详解
    mysql主从数据库配置
    国产免费高性能桌面录屏软件LiveView1.0正式发布
    原来163的大容量异步上传是这么实现的
    C++ string warning C4251
    你不知道的代码规范---摘自《软件随想录》
    ffmpeg avcodec_decode_video2 解码失败
  • 原文地址:https://www.cnblogs.com/zjlcnblogs/p/11134731.html
Copyright © 2011-2022 走看看