[2017雅礼7-6]right 分块

9 篇文章 0 订阅
8 篇文章 0 订阅

稍加分(da)析(biao)可知,当p为奇数时,SG(x)=x&1,当p为偶数时,SG(x)=(x%(p+1)==p)?2:((x%(p+1))&1)。
奇数的话相当于区间翻转,区间异或和,直接线段树即可。
偶数的话分块来处理,每个块维护一个add标记和两个有序数组,分别表示奇余数和偶余数,区间加的时候整块的打标记,零散的直接重构,时间复杂度O(n/s+slogs)。查询的时候整块lower_bound找有多少个1和2,1的话应该是奇数数组中还没+add不超过p的,偶数数组中+p超过了add的,2的话根据add的奇偶性来决定在哪个数组查询,零散的也直接重构,时间复杂度O(nlogs/s+slogs)。
所以我也不知道s该取多少,大概250最优吧。

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define mod (p+1)
using namespace std;
const int maxn=100100;
const int size=220;
int n,p,q,a[maxn];
inline int ID(int x) {return (x-1)/size+1;}
inline int L(int x) {return (x-1)*size+1;}
inline int R(int x) {return min(n,x*size);}
struct tree
{
    tree *ls,*rs;
    int l,r,sum,rev;
    tree()
    {
        ls=rs=NULL;
        sum=rev=0;
    }
    void pushdown()
    {
        if(rev) 
        {
            if((r-l+1)&1)sum^=1;
            rev=0;
            if(ls!=NULL)ls->rev^=1;
            if(rs!=NULL)rs->rev^=1;
        }
    }
    void update()
    {
        ls->pushdown();
        rs->pushdown();
        sum=ls->sum^rs->sum;
    }
    void build(int lx,int rx)
    {
        l=lx;r=rx;
        if(l==r) {sum=(a[l]&1);return ;}
        int mid=(l+r)>>1;
        (ls=new tree)->build(lx,mid);
        (rs=new tree)->build(mid+1,rx);
        update();
    }
    void reverse(int lx,int rx)
    {
        if(l==lx&&r==rx) {rev^=1;return ;}
        pushdown();
        int mid=(l+r)>>1;
        if(rx<=mid) ls->reverse(lx,rx);
        else if(lx>mid) rs->reverse(lx,rx);
        else {ls->reverse(lx,mid); rs->reverse(mid+1,rx);}
        update();
    }
    int query(int lx,int rx)
    {
        pushdown();
        if(l==lx&&r==rx){return sum;};
        int mid=(l+r)>>1;
        if(rx<=mid) return ls->query(lx,rx);
        else if(lx>mid) return rs->query(lx,rx);
        else {return ls->query(lx,mid)^rs->query(mid+1,rx);}
    }
}*xtr;
struct block
{
    int add,rst[2][400],num[2],id;
    void init(int x)
    {
        memset(rst,0x3f3f3f3f,sizeof(rst));
        add=num[0]=num[1]=0;
        id=x;
    }
    void build()
    {
        int temp=add;
        init(id);
        for(int i=L(id);i<=R(id);i++)
        {
            a[i]=(a[i]+temp)%mod;
            if(a[i]&1) rst[1][++num[1]]=a[i];
            else rst[0][++num[0]]=a[i];
        }
        sort(rst[1]+1,rst[1]+num[1]+1);
        sort(rst[0]+1,rst[0]+num[0]+1);
    }
    void modify(int x)
    {
        add=(add+x)%mod;
    }
    void bmodify(int l,int r,int x)
    {
        for(int i=l;i<=r;i++)
            a[i]=(a[i]+x)%mod;
        build();    
    }
    int query()
    {
        bool pd=(add&1);
        int re,lb,ub,lb0,lb1;
        lb0=lower_bound(rst[pd]+1,rst[pd]+num[pd]+2,p+1-add)-rst[pd];
        lb1=lower_bound(rst[!pd]+1,rst[!pd]+num[!pd]+2,p+1-add)-rst[!pd];
        re=((num[pd]-lb0+lb1)&1);
        //cout<<"re1:"<<re<<endl;
        lb=lower_bound(rst[pd]+1,rst[pd]+num[pd]+2,p-add)-rst[pd];
        ub=lb0; 
        re^=(((ub-lb)&1)<<1);
        //cout<<"re2:"<<re<<endl;
        return re;      
    }
    int bquery(int l,int r)
    {
        int re=0;
        build();
        for(int i=l;i<=r;i++)
        {
            if(a[i]==p) re^=2;
            if(a[i]&1) re^=1;
        }   
        return re;
    }
}blk[600];
int main()
{
    freopen("right.in","r",stdin);
    freopen("right.out","w",stdout);
    scanf("%d%d%d",&n,&q,&p);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        a[i]%=(p+1);
    }
    if(p&1) (xtr=new tree)->build(1,n); 
    else
        for(int i=1;i<=ID(n);i++)
        {
            blk[i].init(i);
            blk[i].build();
        }
    while(q--)
    {
        int opt,l,r,x=0;
        scanf("%d%d%d",&opt,&l,&r);
        if(opt==0) 
        {
            scanf("%d",&x);
            x%=mod;
            if(p&1) {if(x&1) xtr->reverse(l,r);}
            else 
            {
                if(ID(l)==ID(r)) blk[ID(l)].bmodify(l,r,x);
                else 
                {
                    blk[ID(l)].bmodify(l,R(ID(l)),x);
                    blk[ID(r)].bmodify(L(ID(r)),r,x);
                    for(int i=ID(l)+1;i<=ID(r)-1;i++)
                        blk[i].modify(x);
                }
            }   
        }
        else
        {
            if(p&1) printf("%d\n",(xtr->query(l,r)!=0));
            else 
            {
                int ans=0;
                if(ID(l)==ID(r)) ans^=blk[ID(l)].bquery(l,r);
                else 
                {
                    ans^=blk[ID(l)].bquery(l,R(ID(l)));
                    ans^=blk[ID(r)].bquery(L(ID(r)),r);
                    for(int i=ID(l)+1;i<=ID(r)-1;i++)
                        ans^=blk[i].query();
                }
                printf("%d\n",(ans!=0));
            }
        }
    }   
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值