「SCOI2015」小凸解密码

通过A,C数组数组确定B数组。断环成链。

修改A数组只影响B数组的四个节点。

求的是离B0最远零区间的距离。

因此用线段树维护B数组的01情况

二分找答案

代码如下

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#define ll long long
#define gc getchar()
#define eps 1e-8
using namespace std;
const int N=2e5+5;
template<class o> inline void qr(o &x) { x=0;char c=gc;int f=1; while(c<'0'||c>'9'){if(c=='-')f=-1;c=gc;} while(c>='0'&&c<='9'){x=x*10+(c^48);c=gc;} x*=f; } template<class o> void qw(o x) { if(x<0)putchar('-'),x=-x; if(x/10)qw(x/10); putchar(x%10+48); } struct node { int l,r,s,c;//c >0的数量 l和r是区间左右端点的01情况 s区间内零区间的数量 node(){} node(int l,int r,int s,int c):l(l),r(r),s(s),c(c){} node operator +(const node a)const { node b; b.l=l,b.r=a.r;b.s=s+a.s;b.c=c+a.c; if(c&&a.c&&(!r||!a.l))b.s++;//左子区间和右子区间都有1,左子区间的右端点为0或者右子区间左端点是0 即对于两区间相交构成的新位置判断是否有零区间生成 return b; } }t[N<<2]; int b[N],a[N];char c[N][2]; void rb(int p,int d) { if(d)t[p]=node(1,1,0,1); else t[p]=node(0,0,0,0); } void build(int p,int l,int r) { if(l==r){rb(p,b[l]);return ;} int mid=l+r>>1; build(p<<1,l,mid); build(p<<1|1,mid+1,r); t[p]=t[p<<1]+t[p<<1|1]; } void change(int p,int l,int r,int x,int d) { if(l==r){rb(p,d);return ;} int mid=l+r>>1; if(x<=mid)change(p<<1,l,mid,x,d); else change(p<<1|1,mid+1,r,x,d); t[p]=t[p<<1]+t[p<<1|1]; } node query(int p,int l,int r,int ql,int qr) { if(ql<=l&&qr>=r)return t[p]; int mid=l+r>>1;node ls,rs;bool g1=0,g2=0; if(ql<=mid)ls=query(p<<1,l,mid,ql,qr),g1=1; if(qr>mid)rs=query(p<<1|1,mid+1,r,ql,qr),g2=1; if(g1&&!g2)return ls; if(!g1&&g2)return rs; return ls+rs; } int calc(int i){return i!=1?(c[i][0]=='*'?a[i]*a[i-1]%10:(a[i]+a[i-1])%10):a[i];} int main() { int n,m;qr(n),qr(m); for(int i=1;i<=n;i++) { qr(a[i]);scanf("%s",c[i]); a[i+n]=a[i];c[i+n][0]=c[i][0]; b[i]=calc(i);b[i+n]=b[i]; } for(int i=1;i<=n;i++)b[i+n]=calc(i+n); build(1,1,n<<1); for(int i=1;i<=m;i++) { int op,x;qr(op),qr(x);++x; if(op==1) { qr(a[x]);scanf("%s",c[x]);a[x+n]=a[x],c[x+n][0]=c[x][0]; b[x]=calc(x),change(1,1,n<<1,x,b[x]); b[x+n]=calc(x+n);change(1,1,n<<1,x+n,b[x+n]); b[x+1]=calc(x+1);change(1,1,n<<1,x+1,b[x+1]); if(x<n)b[x+n+1]=calc(x+n+1),change(1,1,n<<1,x+n+1,b[x+n+1]); } else { if(!a[x]&&!query(1,1,n<<1,x+1,x+n-1).s){puts("0");continue;}//如果当前节点是0,且最大的区间内都没有一个零区间 change(1,1,n<<1,x,a[x]);change(1,1,n<<1,x+n,a[x+n]);//使解密的出发点B0=A0 int l=0,r=n>>1,mid,ans=-2; while(l<=r) { mid=l+r>>1; if(query(1,1,n<<1,x+mid,n+x-mid).s)ans=mid,l=mid+1; else r=mid-1;//区间内没有零区间,扩大范围 } ++ans;//零区间B0的距离定义为:零区间内所有0到B0距离中的最小值,用于计算距离的肯定是最外侧的0,因此计算端点值加1即可。 change(1,1,n<<1,x,b[x]);change(1,1,n<<1,x+n,b[x+n]); printf("====%d\n",ans);puts(""); } } return 0; }

猜你喜欢

转载自www.cnblogs.com/PdrEam/p/12899775.html