UOJ Logo SHYI的博客

博客

[Scoi2010]序列操作 题解

2016-07-30 10:19:07 By SHYI

题目大意:有一个01序列,现在对于这个序列有五种变换操作和询问操作: 0 a b 把[a, b]区间内的所有数全变成0;1 a b 把[a, b]区间内的所有数全变成1;2 a b 把[a,b]区间内的所有数全部取反,也就是说把所有的0变成1,把所有的1变成0;3 a b 询问[a, b]区间内总共有多少个1;4 a b 询问[a, b]区间内最多有多少个连续的1。

思路:维护每一段数的和、左端和右端以及整段中连续的0和1的长度,并使用标记进行下传。

代码:

#include<cstdio>
#include<cstring>
#include<iostream>
#define N 400000
using namespace std;

int root,num,cnt,lmax[2][N],rmax[2][N],mmax[2][N],sum[N],tag[2][N],rev[N],h[N],t[N],rc[N],lc[N],a[N];

void up_date(int x,int k)
{
     int l=lc[x],r=rc[x];
     lmax[k][x]=lmax[k][l],rmax[k][x]=rmax[k][r];
     if (lmax[k][l]==t[l]-h[l]+1) lmax[k][x]+=lmax[k][r];
     if (rmax[k][r]==t[r]-h[r]+1) rmax[k][x]+=rmax[k][l];
     mmax[k][x]=max(max(mmax[k][l],mmax[k][r]),rmax[k][l]+lmax[k][r]);
}

void build(int l,int r,int &cur)
{
     cur=++num,tag[0][cur]=tag[1][cur]=rev[cur]=0;
     h[cur]=l,t[cur]=r;
     if (l==r)
     {
         lc[cur]=rc[cur]=0;
         lmax[0][cur]=rmax[0][cur]=mmax[0][cur]=(a[l]==0);
         lmax[1][cur]=rmax[1][cur]=sum[cur]=mmax[1][cur]=(a[r]==1);
         return;
     }
     int mid=l+r>>1;
     build(l,mid,lc[cur]),build(mid+1,r,rc[cur]);
     up_date(cur,0),up_date(cur,1),sum[cur]=sum[lc[cur]]+sum[rc[cur]];
}

void mark(int x,int k)
{
     tag[k][x]=1,tag[k^1][x]=rev[x]=0,sum[x]=(t[x]-h[x]+1)*k;
     lmax[k][x]=rmax[k][x]=mmax[k][x]=t[x]-h[x]+1;
     lmax[k^1][x]=rmax[k^1][x]=mmax[k^1][x]=0;
}

void re(int x)
{
     rev[x]^=1,sum[x]=t[x]-h[x]+1-sum[x];
     swap(lmax[0][x],lmax[1][x]),swap(rmax[0][x],rmax[1][x]),swap(mmax[0][x],mmax[1][x]);
}

void push_down(int x)
{
     if (tag[0][x]) mark(lc[x],0),mark(rc[x],0),tag[0][x]=0;
     if (tag[1][x]) mark(lc[x],1),mark(rc[x],1),tag[1][x]=0;
     if (rev[x]) re(lc[x]),re(rc[x]),rev[x]=0;
}

void change(int l,int r,int cur,int k)
{
     if (h[cur]>r || t[cur]<l) return;
     if (h[cur]>=l && t[cur]<=r)
     {
         if (k<2) mark(cur,k);
         else re(cur);
         return;
     }
     push_down(cur);
     change(l,r,lc[cur],k),change(l,r,rc[cur],k);
     up_date(cur,0),up_date(cur,1),sum[cur]=sum[lc[cur]]+sum[rc[cur]];
}

int ask1(int l,int r,int cur)
{
     if (h[cur]>r || t[cur]<l) return 0;
     if (h[cur]>=l && t[cur]<=r) return sum[cur];
     push_down(cur);
     return ask1(l,r,lc[cur])+ask1(l,r,rc[cur]);
}

int ask2(int l,int r,int cur)
{
    if (l==h[cur] && r==t[cur]) return cur;
    int mid=h[cur]+t[cur]>>1;
    push_down(cur);
    if (r<=mid) return ask2(l,r,lc[cur]);
    else if (l>mid) return ask2(l,r,rc[cur]);
         else
         {
             int ans=++cnt;
             lc[ans]=ask2(l,mid,lc[cur]),rc[ans]=ask2(mid+1,r,rc[cur]);
             up_date(ans,0),up_date(ans,1),sum[ans]=sum[lc[ans]]+sum[rc[ans]];
             return ans;
         }
}

int main()
{
    int n,m,i,x,y,z;
    scanf("%d%d",&n,&m);
    for (i=1;i<=n;i++) scanf("%d",&a[i]);
    build(1,n,root);
    for (i=1;i<=m;i++)
    {
        scanf("%d%d%d",&z,&x,&y);
        x++,y++;
        if (z==3) printf("%d\n",ask1(x,y,root));
        else if (z==4) cnt=num,printf("%d\n",mmax[1][ask2(x,y,root)]);
             else change(x,y,root,z);
    }
    return 0;
}

评论

暂无评论

发表评论

可以用@mike来提到mike这个用户,mike会被高亮显示。如果你真的想打“@”这个字符,请用“@@”。