「SCOI2015」小凸解密码 解题报告

「SCOI2015」小凸解密码

题意:给一个环,定义一段连续的极长\(0\)串为\(0\)区间,定义一个位置的离一个\(0\)区间的距离为这个位置离这个区间中\(0\)的距离的最小值,每次询问一个位置,求离它最远的\(0\)区间与它的距离,带修改


于是我是多sb才会想到在点分裂平衡树上做类似三分的sb操作?

而且我现在的代码还是错的,只有srand的fhq才能过,不过根据对拍,错误概率很小。

思路,在平衡树上维护\(0\)区间的相对位置

然后每个点维护子树最左区间和子树最右区间

我们把每个询问的时候的那个位置劈开

就变成了两个单峰的上凸的东西

在平衡树上移动就可以了

我似乎还是挂在了相同元素上?

因为写了太久,所以懒得想为啥错了


Code:

#include <cstdio>
#include <cctype>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <algorithm>
using std::min;
using std::max;
const int N=2e5+10;
template <class T>
void read(T &x)
{
    x=0;char c=getchar();
    while(!isdigit(c)) c=getchar();
    while(isdigit(c)) x=x*10+c-'0',c=getchar();
}
int n,m,a[N],b[N];
char c[N][5];
int ch[N][2],L[N],R[N],lL[N],lR[N],rL[N],rR[N],val[N],tot,root;
#define ls ch[now][0]
#define rs ch[now][1]
int New(int l,int r)
{
    val[++tot]=rand(),lL[tot]=rL[tot]=L[tot]=l,lR[tot]=rR[tot]=R[tot]=r;
    return tot;
}
void updata(int now)
{
    lL[now]=rL[now]=L[now];
    lR[now]=rR[now]=R[now];
    if(ls) lL[now]=lL[ls],lR[now]=lR[ls];
    if(rs) rL[now]=rL[rs],rR[now]=rR[rs];
}
void split(int now,int &x,int &y,int k)
{
    if(!now){x=y=0;return;}
    if(L[now]<=k) x=now,split(rs,rs,y,k);
    else y=now,split(ls,x,ls,k);
    updata(now);
}
int Merge(int x,int y)
{
    if(!x||!y) return x^y;
    if(val[x]<val[y])
    {
        ch[x][1]=Merge(ch[x][1],y);
        updata(x);
        return x;
    }
    else
    {
        ch[y][0]=Merge(x,ch[y][0]);
        updata(y);
        return y;
    }
}
void Insert(int now)
{
    int x,y;
    split(root,x,y,L[now]);
    root=Merge(x,Merge(now,y));
}
void build()
{
    int las=-1;
    for(int i=0;i<n;i++)
    {
        if(las==-1&&!b[i]) las=i;
        if(~las&&b[i]) Insert(New(las,i-1)),las=-1;
    }
    if(~las) Insert(New(las,n-1));
}
int getl(int now)
{
    if(ls) return getl(ls);
    return now;
}
int getr(int now)
{
    if(rs) return getr(rs);
    return now;
}
void modi(int pos,int typ)
{
    if(typ)
    {
        if(c[pos][0]=='+') b[pos]=(a[pos?pos-1:n-1]+a[pos])%10;
        else b[pos]=a[pos?pos-1:n-1]*a[pos]%10;
    }
    int x,y,z;
    if(b[pos])
    {
        split(root,x,y,pos);
        int now=getr(x);
        if(pos>R[now])
        {
            root=Merge(x,y);
            return;
        }
        if(L[now]==R[now]) split(x,x,z,L[now]-1);
        else
        {
            if(L[now]==pos) ++L[now],updata(now);
            else if(R[now]==pos) --R[now],updata(now);
            else
            {
                z=New(pos+1,R[now]);
                R[now]=pos-1;
                updata(now);
                x=Merge(x,z);
            }
        }
        root=Merge(x,y);
    }
    else
    {
        split(root,x,y,pos);
        int nowl=getr(x),nowr=getl(y);
        if(L[nowl]<=pos&&pos<=R[nowl]){root=Merge(x,y);return;}
        if(R[nowl]+1==pos) ++R[nowl],updata(nowl);
        else if(L[nowr]-1==pos) --L[nowr],updata(nowr);
        else {root=Merge(x,Merge(New(pos,pos),y));return;}
        if(R[nowl]==L[nowr]-1)
        {
            R[nowl]=R[nowr];
            updata(nowl);
            split(y,z,y,R[nowr]);
        }
        root=Merge(x,y);
    }
}
void change()
{
    int pos;
    read(pos),read(a[pos]),scanf("%s",c[pos]);
    modi(pos,1);
    modi(pos==n-1?0:pos+1,1);
}
int dis(int l,int r,int p)
{
    return l<=p&&p<=r?0:(p<l?min(l-p,p+n-r):min(p-r,l+n-p));
}
int ans,le,ri;
void query(int now,int p)
{
    if(!now) return;
    int dn=dis(L[now],R[now],p);
    ans=max(ans,dn);
    if(ls)
    {
        int dl=dis(rL[ls],rR[ls],p);
        if(dn<=dl) query(ls,p);
    }
    if(rs)
    {
        int dr=dis(lL[rs],lR[rs],p);
        if(dn<=dr) query(rs,p);
    }
}
void qry()
{
    int pos;ans=0;
    read(pos);
    b[pos]=a[pos];
    modi(pos,0);
    if(!root)
    {
        puts("-1");
        modi(pos,1);
        return;
    }
    le=getl(root),ri=getr(root);
    if(le==ri)
    {
        printf("%d\n",dis(L[le],R[ri],pos));
        modi(pos,1);
        return;
    }
    if(L[le]==0&&R[ri]==n-1)
    {
        ans=min(dis(0,R[le],pos),dis(L[ri],n-1,pos));
        split(root,le,root,0);
        split(root,root,ri,L[ri]-1);
    }
    else le=ri=0;
    int x,y;
    split(root,x,y,pos);
    query(x,pos),query(y,pos);
    root=Merge(x,y);
    root=Merge(le,Merge(root,ri));
    modi(pos,1);
    printf("%d\n",ans);
}
int main()
{
    srand(time(0));
    memset(L,0x3f,sizeof L);
    memset(R,0x3f,sizeof R);
    read(n),read(m);
    for(int i=0;i<n;i++) read(a[i]),scanf("%s",c[i]);
    for(int i=0;i<n;i++)
    {
        if(c[i][0]=='+') b[i]=(a[i?i-1:n-1]+a[i])%10;
        else b[i]=a[i?i-1:n-1]*a[i]%10;
    }
    build();
    for(int op,i=1;i<=m;i++)
    {
        read(op);
        if(op==1) change();
        else qry();
    }
    return 0;
}

2019.2.28

转载于:https://www.cnblogs.com/butterflydew/p/10450531.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值