分块

写到一半的博客突然消失QAQ

心态爆炸

所以我们直接上例题吧

原理有空一定补上

update:咳咳咳,直接补上xzy巨佬的博客
一、大水题

用啥线段树

用分块可以轻松水过

上代码

 

#include<bits/stdc++.h>
using namespace std;
namespace _mzf
{
    #define ll long long
    const ll N=2e6+100;
    ll n,m,sq,q[N],cnt[N],sum[N];
    ll a[N];
    ll read()
    {
        ll sum=0,flag=1;
        char ch=getchar();
        while(ch<'0'||ch>'9')
        {
            if(ch=='-') flag=-1;
            ch=getchar();
        }
        while(ch>='0'&&ch<='9')
        {
            sum=sum*10+ch-'0';
            ch=getchar();
        }
        return flag*sum;
    }
    void update(ll l,ll r,ll k)
    {
        ll ans=0;
        for(int i=l;i<=min(q[l]*sq,r);i++)
        {
            a[i]+=k;
            sum[q[i]]+=k;
        }
        if(q[l]!=q[r])
        {
            for(int i=(q[r]-1)*sq+1;i<=r;i++)
            {
                a[i]+=k;
                sum[q[i]]+=k;
            }            
        }
        for(int i=q[l]+1;i<q[r];i++) 
        {
            cnt[i]+=k;
            sum[i]+=k*sq;
        }
    }
    ll query(ll l,ll r)
    {
        ll ans=0;
        for(int i=l;i<=min(q[l]*sq,r);i++)ans+=a[i]+cnt[q[i]];
        if(q[l]!=q[r])    for(int i=(q[r]-1)*sq+1;i<=r;i++)ans+=a[i]+cnt[q[i]];
        for(int i=q[l]+1;i<q[r];i++)
        ans+=sum[i];
        return ans;
    }
    void mzfmain()
    {
        n=read();
        m=read();
        sq=sqrt(n);
        for(int i=1;i<=n;i++)
        {
            q[i]=(i-1)/sq+1;
            a[i]=read();
            sum[q[i]]+=a[i];
        }
        while(m--)
        {
            ll op,x,y,k;
            op=read();
            if(op==1)
            {
                x=read();
                y=read();
                k=read();
                update(x,y,k);
            }
            else 
            {
                x=read();
                y=read();
                printf("%lld\n",query(x,y));
            }
        }
    }
}
int main()
{
    _mzf::mzfmain();
    return 0;
}

 

二、超级大水题

板子题

几乎是直接把上面的代码粘过来就能过

代码就不放了(太难看

三、开关

用啥线段树

分块轻松吊打(大部分)线段树

每个灯都只有0,1两个状态

仍然是散块直接暴力修改+整块修改打标记

开关灯操作怎么办?当然是位运算。

取反?异或更方便!

tag数组异或1可以很方便的标记元素被反转多少次

最后询问的时候异或上即可

然后你就做完了

#include<bits/stdc++.h>
using namespace std;
namespace _mzf
{
    #define ll long long
    const ll N=2e5+100;
    ll sq,n,m,q[N],cnt[N],sum[N],a[N];
    ll read()
    {
        ll sum=0,flag=1;
        char ch=getchar();
        while(ch<'0'||ch>'9')
        {
            if(ch=='-') flag=-1;
            ch=getchar();
        }
        while(ch>='0'&&ch<='9')
        {
            sum=sum*10+ch-'0';
            ch=getchar();
        }
        return flag*sum;
    }
    void update(ll l,ll r)
    {
        for(int i=l;i<=min(q[l]*sq,r);i++)
        {
            sum[q[l]]-=(a[i]^cnt[q[l]]);
            a[i]^=1;
            sum[q[l]]+=(a[i]^cnt[q[l]]);
        }
        if(q[l]!=q[r])
        for(int i=(q[r]-1)*sq+1;i<=r;i++)
        {
            sum[q[r]]-=(a[i]^cnt[q[r]]);
            a[i]^=1;
            sum[q[r]]+=(a[i]^cnt[q[r]]);
        }
        for(int i=q[l]+1;i<q[r];i++)
        {
            sum[i]=sq-sum[i];
            cnt[i]^=1;
        }
    }
    ll query(ll l,ll r)
    {
        ll ans=0;
        for(int i=l;i<=min(q[l]*sq,r);i++) ans+=(a[i]^cnt[q[l]]);
        if(q[l]!=q[r])
        for(int i=(q[r]-1)*sq+1;i<=r;i++) ans+=(a[i]^cnt[q[r]]);
        for(int i=q[l]+1;i<q[r];i++)
        ans+=sum[i];
        return ans;
    }
    void mzfmain()
    {
        n=read();
        m=read();
        sq=sqrt(n);
        for(int i=1;i<=n;i++) q[i]=(i-1)/sq+1;
        for(int i=1;i<=m;i++)
        {
            ll a,b,c;
            c=read();
            if(!c)
            {
                a=read();
                b=read();
                update(a,b);
            }
            else
            {
                a=read();
                b=read();
                cout<<query(a,b)<<endl;
            }
        }
    }
}
int main()
{
    _mzf::mzfmain();
    return 0;
}

 

posted @ 2021-11-19 17:19  云山千叠  阅读(34)  评论(0编辑  收藏  举报