bzoj4447: [Scoi2015]小凸解密码

Description

小凸得到了一个密码盘,密码盘被等分成N个扇形,每个扇形上有一个数字(0~9),和一个符号(“+”或”*”)
密码盘解密的方法如下:
首先,选择一个位置开始,顺时针地将数字和符号分别记在数组A和数组C巾
解密的方法如下
B0=A0
当x>0时:
若Cx为“+”,Bx=(Ax+Ax-1)%10,注意:x-1是下标值
若Cx为“*”,Bx= (Ax×Ax-1)%10,注意:x-1是下标值
操作完成后,可以得到一个长度为n的数组B,然后以B0为起点将B数组顺时针写成一个环,解密就完成
了,称得到的环为答案环。
现在小凸得到了一份指令表,指令表上有2种操作。
一种指令是修改操作,即改变原来密码盘上一个位置的数字和符号。
另一种指令是询问操作,具体如下:
首先从指令给出的位置开始完成解密,得到答案环。
答案环上会有一些0连在一起,将这些连在一起的0称为零区间,找出其中距离B0最远的那个零区间,输
出这个距离。
零区问和B0的距离定义为:零区问内所有0到B0距离中的最小值。

Input

第1行包含2个整数n,m,代表密码盘大小和指令个数
接下来n行,每行包含1个整数和1个字符,按顺时针顺序给出了密码盘上的数组和符号
接下来m行,依次给出指令
每行第1个整数代表指令类型
若第1个墼数为1,代表本行对应指令为修改操作,之后依次有2个整数pos,num和1个字符opt,分别
代表修改的位置,以及修改后该位置的数字和字符
若第1个整数为2,代表本行对应指令位询问操作,之后有1个整数pos,代表本次操作中解密的开始位置
密码盘上的位置标号为0到n-l
数据保证合法,即数据中0≤pos

Output

对于每个询问操作1行,输出答案,若答案环上没有0,输出-1

Sample Input

5 8

0 *

0 *

0 *

0 *

0 *

2 0

1 0 1 +

1 2 1 +

2 3

1 1 1 +

1 3 1 +

1 4 1 +

2 4

Sample Output

0

2

-1

HINT

第1个询问,答案环为[0,0,0,0,0],仅有1个零区间,且B0在其中,所以距离是0

对于第2个询问,答案环为[0,0,1,0,l],有2个零区间,(0,1)和B0距离是o,(3,3)和B0距离是2,故答案为2

对于第3个询问,答案环为[1,2,2,2,2],没有零区间,答案是-1

对于100%数据,5 <=n,m≤10^5

解题思路:

先预处理出所有b[i]的值,那么修改就会改变b[i]和b[i+1],询问相当于把b[i]变成a[i]。
用set维护一下0区间,每次找pos+n/2旁边的几个区间计算距离取max即可。

#include<bits/stdc++.h>
using namespace std; 

int getint()
{
    int i=0,f=1;char c;
    for(c=getchar();(c!='-')&&(c<'0'||c>'9');c=getchar());
    if(c=='-')f=-1,c=getchar();
    for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
    return i*f;
}

const int N=100005;
int n,m,a[N],b[N],c[N],cnt;
struct node
{
    int l,r;
    node(int _l=0,int _r=0):l(_l),r(_r){};
    inline friend bool operator < (const node &a,const node &b)
    {return a.l<b.l;}
};
set<node>S;
#define It set<node>::iterator
inline int nxt(int i){return i==n?1:i+1;}
inline int bef(int i){return i==1?n:i-1;}
inline It Nxt(It it){return it==--(--S.end())||it==--S.end()?++S.begin():++it;}
inline It Bef(It it){return it==++S.begin()||it==S.begin()?--(--S.end()):--it;}
It it;

void Insert(int p)
{
    node t=node(p,p),suf,pre;
    it=S.lower_bound(t),suf=*it;
    if(suf.l!=n+1&&suf.l==p+1)S.erase(it),t.r=suf.r;
    it=--S.lower_bound(t),pre=*it;
    if(pre.r&&pre.r==p-1)S.erase(it),t.l=pre.l;
    S.insert(t);
}
void Delete(int p)
{
    node t=node(p,p),cur;
    it=--S.upper_bound(t),cur=*it,S.erase(it);
    if(cur.l<p)S.insert(node(cur.l,p-1));
    if(cur.r>p)S.insert(node(p+1,cur.r));
} 
void modify(int p,int x,int y)
{
    if(x&&!y)Insert(p);
    if(!x&&y)Delete(p);
}
bool in(int l,int r,int p)
{
    if(l<=r)return l<=p&&p<=r;
    else return l<=p||p<=r;
}
int calc(int l,int r,int p)
{
    if(in(l,r,p))return 0;
    return min(min(abs(p-l),abs(p-r)),min(n-abs(p-r),n-abs(p-l)));
}
int solve(node tmp,int p)
{
    if(tmp.l==1)
    {
        node t=*--(--S.end());
        if(t.r==n)tmp.l=t.l;
    }
    if(tmp.r==n)
    {
        node t=*++S.begin();
        if(t.l==1)tmp.r=t.r;
    }
    return calc(tmp.l,tmp.r,p);
}
int query(int p)
{
    if(S.size()==2)return -1;
    int pos=p+n/2;if(pos>n)pos-=n;
    node t=node(pos,pos),suf,pre;
    it=S.lower_bound(t);
    int res=0;
    it=Bef(Bef(Bef(it))); 
    for(int i=1;i<=6;i++)
        res=max(res,solve(*it,p)),it=Nxt(it);
    return res;
}
int main()
{
    //freopen("lx.in","r",stdin);
    //freopen("lx.out","w",stdout);
    n=getint(),m=getint();
    for(int i=1;i<=n;i++)a[i]=getint(),c[i]=(getchar()=='+'?0:1);
    for(int i=1;i<=n;i++)b[i]=(c[i]?a[bef(i)]*a[i]%10:(a[bef(i)]+a[i])%10);
    S.insert(node(0,0)),S.insert(node(n+1,n+1));
    for(int i=1;i<=n;i++)if(!b[i])Insert(i);
    while(m--)
    {
        int op=getint(),p=getint()+1,x=0,y=0;
        if(op==1)
        {
            a[p]=getint(),c[p]=(getchar()=='+'?0:1);
            x=b[p],y=b[p]=(c[p]?a[bef(p)]*a[p]%10:(a[bef(p)]+a[p])%10);
            modify(p,x,y);
            x=b[nxt(p)],y=b[nxt(p)]=(c[nxt(p)]?a[p]*a[nxt(p)]%10:(a[p]+a[nxt(p)])%10);
            modify(nxt(p),x,y);
        }
        else 
        {
            modify(p,b[p],a[p]);
            printf("%d\n",query(p));
            modify(p,a[p],b[p]); 
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值