[jzoj4665][CF407E]k-d-sequence

题目大意

给你一个长度为n的序列a[],两个参数k,d,求序列最长的一段,使得这一段所有的数升序排序后最多再加上k个任意的数,可以构成公差为d的等差数列。输出这一段的两端。
n<=2*10^5, 0<=k<=2*10^5, d,|a[i]|<=10^9

分析

首先有个很显然的结论嘛,答案的那一段区间所有数,两两之间的差都是d的倍数,而且互不相同。那么前者等价于相邻的差是d的倍数,于是乎我们可以把原序列切成许多段,每一段的数相邻的差都是d的倍数。
后者可以设mxright[x]表示最大的满足a[x~mxright[x]]互不相同的值,求答案的时候限制一下就好了。
现在考虑一段上怎么做。
为了看起来好看,激发我们的灵感,我们可以先处理一下数据。对于一段每个数a[x],令a[x]=(a[x]-min)/d+1,min即为这一段的最小值。
接下来怎么做?二分长度?不行,不满足二分性质。
考虑一下暴力如何求解先吧!枚举左右端点l,r,满足什么样的条件可以更新答案呢?
即max{a[l~r]}-min{a[l~r]}+1≤r-l+1+k。

而做这些题的套路,一般是枚举一个左端点,看看合法最远右端点在哪里。
我们化一下上面的式子,max{a[l~r]}-min{a[l~r]}-r≤k-l
这样右边就只与左端点有关了。
我们考虑枚举左端点l。这时设mx[r]=max{a[l~r]},mn[r]=min{a[l~r]},很容易发现mx是不下降的,mn是不上升的,是不是可以维护一下他们呢?设b[r]=mx[r]-mn[r]-r;看一下怎么维护它,加入能维护,那么我们只要找最大的r,满足b[r]≤k-l。
倒着枚举左端点,这样可以让mx只会变大而不缩小,更好维护;mn同理。考虑l变成l-1会发生什么?就是mx(mn)中,大于(小于)a[l-1]的,全部变为a[l-1]。哎,我们发现会出现一块一块的东西,以后修改的时候不就可以区间修改了?因此我们用栈记录每种值出现的一块的位置,这样到时候更新mx(mn)就很方便了,每次弹出一块,区间加(减)。事实上,我们只用线段树维护b就好了。
那么整理一下做法:枚举右端点l,用线段树维护b数组,区间加减,区间位置由栈来维护。寻找最大r满足b[r]≤k-l,且r≤mxright[l]。时间复杂度O(nlogn)

代码

打的时候生病了,很乱。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<map>
using namespace std;
typedef long long ll;
#define fo(i,j,k) for(i=j;i<=k;i++)
#define fd(i,j,k) for(i=j;i>=k;i--)
const int M=200005,mx=1e9+7,N=1e8+7;
struct rec
{
    int mn,tag;
}tr[M*5];
int a[M],n,K,d,sta1[M],sta2[M],b[M],t1,t2,i,j,k,l,r,L,R,tt,mn,ans,x,maxr[M],dur;//store pos
map<int,int> pos;
void down(int x,int l,int r)
{
    if (l!=r)
    {
        tr[x*2].tag+=tr[x].tag;
        tr[x*2+1].tag+=tr[x].tag;
    }
    tr[x].mn+=tr[x].tag;
    tr[x].tag=0;
}
void downdate(int x,int l,int r,int m)
{
    down(x,l,r);
    if (l!=r) 
    {
        down(x*2,l,m);
        down(x*2+1,m+1,r);
    }
    if (l!=r)
        tr[x].mn=min(tr[x*2].mn,tr[x*2+1].mn);
}
void make(int x,int l,int r)
{
    tr[x].mn=N;
    if (l==r)
        return;
    int m=(l+r)/2;
    make(x*2,l,m);
    make(x*2+1,m+1,r);
}
void change(int x,int l,int r,int i,int j,int val)
{
    int m=(l+r)/2;
    if (l==i&&r==j)
    {
        tr[x].tag+=val;
        downdate(x,l,r,m);
        return;
    }
    downdate(x,l,r,m);
    if (j<=m)
        change(x*2,l,m,i,j,val);
    else if (i>m)
        change(x*2+1,m+1,r,i,j,val);
    else
    {
        change(x*2,l,m,i,m,val);
        change(x*2+1,m+1,r,m+1,j,val);
    }
    tr[x].mn=min(tr[x*2].mn,tr[x*2+1].mn);
}
void maintain_mx(int x)
{
    while (t1&&a[x]>a[sta1[t1]])
    {
        change(1,1,n,sta1[t1],sta1[t1-1]-1,a[x]-a[sta1[t1]]);
        sta1[t1--]=0;
    }
    sta1[++t1]=x;
}
void maintain_mn(int x)
{
    while (t2&&a[x]<a[sta2[t2]])
    {
        change(1,1,n,sta2[t2],sta2[t2-1]-1,a[sta2[t2]]-a[x]);
        sta2[t2--]=0;
    }
    sta2[++t2]=x;
}
int find(int x,int l,int r,int val)
{
    int m=(l+r)/2;
    downdate(x,l,r,m);
    if (tr[x].mn>val) return 0;
    if (l==r) return l;
    if (tr[x*2+1].mn<=val) 
        return find(x*2+1,m+1,r,val);
    else return find(x*2,l,m,val);
}
int main()
{
    //freopen("t0.in","r",stdin);
    scanf("%d %d %d",&n,&K,&d);
    fo(i,1,n) scanf("%d",a+i);
    R=r=n;
    //make(1,1,n);
    b[++b[0]]=n;
    sta1[0]=n+1;
    sta2[0]=n+1;
    fd(i,n,1)
    {
        if (a[i]==a[i+1])
            tt++;
        else tt=1;
        if ((!d)&&tt>=ans) 
        {
            ans=tt;
            L=i;
        }
        if (x=pos[a[i]])
            r=min(x-1,r);
        pos[a[i]]=i;
        maxr[i]=r;
        if (d&&((a[i]%d+d)%d!=(a[i-1]%d+d)%d||i==1)) 
        {
            b[++b[0]]=i-1;
            r=i-1;
            mn=mx;
            fo(j,i,R)
                mn=min(mn,a[j]);
            fo(j,i,R) 
                a[j]=(a[j]-mn)/d+1;
            R=i-1;
        }
    }
    R=n;
    if (d)
    fo(k,1,b[0])
    {
        //clear();
        fd(i,b[k],b[k+1]+1)
        {
            // a[i]
            if (i==1) 
            {
                i=1;    
            }
            maintain_mx(i);
            maintain_mn(i);
            if (R>maxr[i]) 
            {
                change(1,1,n,maxr[i]+1,R,N);
                R=maxr[i];
            }
            change(1,1,n,i,i,-i);//mx-mn-r
            dur=find(1,1,n,K-i);// find the rightermost one which is less than k-l
            if (dur-i+1>=ans)
            {
                ans=dur-i+1;
                L=i;
            }
        }
    }
    //min max for b[i] l-- r query change add delete
    printf("%d %d\n",L,L+ans-1);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值