[BZOJ]4527: K-D-Sequence (Codeforces 407E) 线段树

Description

我们称一个数列为一个好的k-d数列,当且仅当我们在其中加上最多k个
数之后,数列排序后为一个公差为d的等差数列。
你手上有一个由n个整数组成的数列a。你的任务是找到它的最长连续子
串,使得满足子串为好的k-d数列。

Solution

d = 0 d=0 d=0先特判。
否则一个区间满足条件,当且仅当:
1、区间内的数 % d \%d %d相等。
2、区间内没有重复的数。
3、 m a x − m i n d ≤ r − l + k {max-min\over d}\le r-l+k dmaxminrl+k
把第三个条件再化一下,把原序列每个数变为 ÷ d \div d ÷d向下取整,那么条件变为 m a x − m i n ≤ r − l + k max-min\le r-l+k maxminrl+k
枚举 r r r,第一个条件直接一边扫看向左最多伸到哪里,第二个条件先求出每个位置的数上一次出现位置 p r e i pre_i prei,一个区间 [ l , r ] [l,r] [l,r]没有重复的数就是区间 p r e i pre_i prei最大值 &lt; l &lt;l <l,二分+ R M Q RMQ RMQ即可。
对于第三个条件,我的做法比较复杂,用线段树维护每个位置当前到 r r r的最大最小值,询问时就在线段树上二分第一个 m a x − m i n + l ≤ r + k max-min+l\le r+k maxmin+lr+k的位置。
网上的题解似乎都是用一个单调栈辅助,确实简单了许多,线段树不用维护太多东西,只是一些基本操作。
一开始判断余数相同这个条件,脑抽了用绝对值 % d \%d %d的余数来判断……调了好久……

Code

#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define pa pair<int,int>
const int Maxn=200010;
const int inf=2147483647;
int read()
{
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
    return x*f;
}
map<int,int>pos;
int n,k,d,a[Maxn],b[Maxn],pre[Maxn],f[Maxn][18],Log[Maxn];
int query_mx(int l,int r)
{
    int t=Log[r-l+1];
    return max(f[l][t],f[r-(1<<t)+1][t]);
}
struct Seg{int l,r,lc,rc,c[3],cmx,cmx1,cmn,cmn1,tmx,tmn;}tr[Maxn<<1];
int tot=0;
void wmx(int x,int v)
{
    if(v<tr[x].cmx)return;
    tr[x].cmx1=tr[x].cmx=v;tr[x].c[2]=tr[x].l+v;tr[x].c[0]=tr[x].c[1]+v;tr[x].tmx=v;
}
void wmn(int x,int v)
{
    if(v>tr[x].cmn)return;
    tr[x].cmn1=tr[x].cmn=v;tr[x].c[1]=tr[x].l-v;tr[x].c[0]=tr[x].c[2]-v;tr[x].tmn=v;
}
void up(int x)
{
    int lc=tr[x].lc,rc=tr[x].rc;
    tr[x].cmx=max(tr[lc].cmx,tr[rc].cmx);
    tr[x].cmn=min(tr[lc].cmn,tr[rc].cmn);
    tr[x].cmx1=min(tr[lc].cmx1,tr[rc].cmx1);
    tr[x].cmn1=max(tr[lc].cmn1,tr[rc].cmn1);
    for(int i=0;i<3;i++)tr[x].c[i]=min(tr[lc].c[i],tr[rc].c[i]);
}
void down(int x)
{
    int t=tr[x].tmx;
    if(t!=inf)wmx(tr[x].lc,t),wmx(tr[x].rc,t),tr[x].tmx=inf;
    t=tr[x].tmn;
    if(t!=inf)wmn(tr[x].lc,t),wmn(tr[x].rc,t),tr[x].tmn=inf;
}
void build(int l,int r)
{
    int x=++tot;
    tr[x].l=l;tr[x].r=r;tr[x].tmx=tr[x].tmn=inf;
    if(l==r)return;
    int mid=l+r>>1;
    tr[x].lc=tot+1,build(l,mid);
    tr[x].rc=tot+1,build(mid+1,r);
}
void w(int x,int p)
{
    if(tr[x].l==tr[x].r)
    {
        tr[x].cmx=tr[x].cmn=tr[x].cmx1=tr[x].cmn1=b[p];
        tr[x].c[0]=p;tr[x].c[1]=p-b[p];tr[x].c[2]=p+b[p];
        return;
    }
    int mid=tr[x].l+tr[x].r>>1,lc=tr[x].lc,rc=tr[x].rc;
    down(x);
    if(p<=mid)w(lc,p);
    else w(rc,p);
    up(x);
}
int tmp[30],lt;
void _w(int x,int l,int r,int v,int o)
{
    if(tr[x].l==l&&tr[x].r==r)
    {
        if(o==1)wmx(x,v);
        if(o==2)wmn(x,v);
        if(o==3)tmp[++lt]=x;
        return;
    }
    int mid=tr[x].l+tr[x].r>>1,lc=tr[x].lc,rc=tr[x].rc;
    down(x);
    if(r<=mid)_w(lc,l,r,v,o);
    else if(l>mid)_w(rc,l,r,v,o);
    else _w(lc,l,mid,v,o),_w(rc,mid+1,r,v,o);
    up(x);
}
int V;
int go(int x,int op)
{
    if(tr[x].l==tr[x].r)return x;
    int mid=tr[x].l+tr[x].r>>1,lc=tr[x].lc,rc=tr[x].rc;
    down(x);
    if(op==1)
    {
        if(tr[lc].c[0]<=V)return go(lc,op);
        return go(rc,op);
    }
    else if(op==2)
    {
        if(tr[lc].cmn1>=V)return go(lc,op);
        return go(rc,op);
    }
    else
    {
        if(tr[lc].cmx1<=V)return go(lc,op);
        return go(rc,op);
    }
}
int main()
{
    n=read(),k=read(),d=read();
    for(int i=1;i<=n;i++)
    {
        a[i]=read();
        pre[i]=pos[a[i]];pos[a[i]]=i;
    }
    int ans=1,L=1,R=1,v=1;
    if(d==0)
    {
        for(int i=2;i<=n;i++)
        {
            if(a[i]==a[i-1])v++;
            else v=1;
            if(v>ans)ans=v,R=i,L=i-v+1;
        }
        return printf("%d %d",L,R),0;
    }
    Log[1]=0;for(int i=2;i<=n;i++)Log[i]=Log[i>>1]+1;
    for(int i=1;i<=n;i++)f[i][0]=pre[i];
    for(int j=1;(1<<j)<=n;j++)
    for(int i=1;i+(1<<j)-1<=n;i++)
    f[i][j]=max(f[i][j-1],f[i+(1<<(j-1))][j-1]);
    for(int i=1;i<=n;i++)b[i]=(int)floor((double)a[i]/(double)d);
    build(1,n);w(1,1);
    for(int i=2;i<=n;i++)
    {
        if((a[i]%d+d)%d==(a[i-1]%d+d)%d)v++;
        else v=1;
        if(v>1)
        {
            lt=0;_w(1,i-v+1,i-1,0,3);
            int op=(b[i]>b[i-1]);
            bool flag=false;
            for(int j=lt;j;j--)
            {
                int x=tmp[j];
                if(op==1)
                {
                    if(tr[x].cmx<=b[i])wmx(x,b[i]);
                    else{V=b[i];int o=go(x,3);_w(1,tr[o].l,tr[x].r,b[i],1);break;}
                }
                else
                {
                    if(tr[x].cmn>=b[i])wmn(x,b[i]);
                    else{V=b[i];int o=go(x,2);_w(1,tr[o].l,tr[x].r,b[i],2);break;}
                }
            }
        }
        w(1,i);
        if(v<=R-L+1)continue;
        int l=i-v+1,r=i;bool flag=false;
        while(l<=r)
        {
            int mid=l+r>>1;
            if(query_mx(mid,i)<mid)r=mid-1,flag=true;
            else l=mid+1;
        }
        if(!flag)continue;
        r++;lt=0;
        _w(1,r,i,0,3);V=i+k;
        for(int j=1;j<=lt;j++)
        {
            int x=tmp[j];
            if(tr[x].c[0]<=V)
            {
                int o=go(x,1);
                if(i-tr[o].l+1>R-L+1)R=i,L=tr[o].l;
                break;
            }
        }
    }
    printf("%d %d",L,R);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值