题目描述
小凸得到了一个密码盘,密码盘被等分成 nn 个扇形,每个扇形上有一个数字 (0 \sim 9)(0∼9),和一个符号 (( + 或 * ))。密码盘解密的方法如下:
首先,选择一个位置开始,顺时针地将数字和符号分别记在数组 AA 和数组 CC 中。解密的方法如下:
B_0 = A_0B
0
=A
0
当 x > 0x>0 时:
若 C_xC
x
为 +,B_x = (A_x + A_{x - 1}) % 10B
x
=(A
x
+A
x−1
)%10
若 C_xC
x
为 *,B_x = (A_x \times A_{x - 1}) % 10B
x
=(A
x
×A
x−1
)%10
操作完成后,可以得到一个长度为 nn 的数组 BB,然后以 B_0B
0
为起点将 BB 数组顺时针写成一个环,解密就完成了,称得到的环为答案环。
现在小凸得到了一份指令表,指令表上有 2 种操作。一种指令是修改操作,即改变原来密码盘上一个位置的数字和符号。另一种指令是询问操作,具体如下:
首先从指令给出的位置开始完成解密,得到答案环。
答案环上会有一些 00 连在一起,将这些连在一起的 00 称为零区间,找出其中距离 B_0B
0
最远的那个零区间,输出这个距离(零区间和 B_0B
0
的距离定义为:零区间内所有 00 到 B_0B
0
距离中的最小值)。
分析
本题较为烧脑,请脑细胞不够的同学直接学习代码。
- 解密的方法如下:
B_0 = A_0B
0
=A
0
当 x > 0x>0 时:
若 C_xC
x
为 +,B_x = (A_x + A_{x - 1}) % 10B
x
=(A
x
+A
x−1
)%10
若 C_xC
x
为 *,B_x = (A_x \times A_{x - 1}) % 10B
x
=(A
x
×A
x−1
)%10
操作完成后,可以得到一个长度为 nn 的数组 BB,然后以 B_0B
0
为起点将 BB 数组顺时针写成一个环,解密就完成了,称得到的环为答案环。
- 数据范围:
对于 20 %20% 数据,5 \leq n \leq 10^5, 5 \leq m \leq 10005≤n≤10
5
,5≤m≤1000;
对于 60 %60% 数据,5 \leq n \leq 10^5, 5 \leq m \leq 10^45≤n≤10
5
,5≤m≤10
4
;
对于 100 %100% 数据,5 \leq n, m \leq 10^55≤n,m≤10
5
。
AC代码
#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;
}
函数还是较难看懂的,主程序更难看懂,因为题目等级是NOI等级(NOIP是省赛),我还想给它IOI or root等级(最高等级NOI+/CTSC)。