数列分块入门

1. 数列分块入门1

区间修改,单点查询

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN=5e4+5;
int n,len,cnt;
int a[MAXN],tag[MAXN];
int pos[MAXN],l[MAXN],r[MAXN];
inline void add(int x,int y,int k)
{
    if(x>y)return;
    if(pos[x]==pos[y])
    {
        for(register int i=x;i<=y;i++)
            a[i]+=k;
        return;
    }
    for(register int i=x;i<=r[pos[x]];i++)a[i]+=k;
    for(register int i=l[pos[y]];i<=y;i++)a[i]+=k;
    for(register int i=pos[x]+1;i<=pos[y]-1;i++)tag[i]+=k;
}
signed main()
{
    scanf("%lld",&n);
    len=sqrt(n);
    cnt=n/len+(n%len!=0);
    for(register int i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
        pos[i]=(i-1)/len+1;
    }
    for(register int i=1;i<=cnt;i++)
    {
        l[i]=(i-1)*len+1;
        r[i]=i*len;
    }
    for(register int i=1;i<=n;i++)
    {
        int op,x,y,c;
        scanf("%lld%lld%lld%lld",&op,&x,&y,&c);
        if(op==0)add(x,y,c);
        if(op==1)printf("%lld\n",a[y]+tag[pos[y]]);
    }
    return 0;
}

2. 数列分块入门2

区间修改,询问区间中小于 \(c\) 的数的个数

\(b\) 数组记录每个块排序之后的数组,以便二分查询排名

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN=1e5+5;
int n,len,cnt;
int a[MAXN],b[MAXN],tag[MAXN];
int pos[MAXN],l[MAXN],r[MAXN];
inline void add(int x,int y,int k)
{
    if(x>y)return;
    if(pos[x]==pos[y])
    {
        for(register int i=x;i<=y;i++)
            a[i]+=k;
        return;
    }
    for(register int i=x;i<=r[pos[x]];i++)a[i]+=k;
    for(register int i=l[pos[y]];i<=y;i++)a[i]+=k;
    for(register int i=pos[x]+1;i<=pos[y]-1;i++)tag[i]+=k;
}
inline int ask(int x,int y,int k)
{
    int sum=0;
    if(x>y)return 0;
    if(pos[x]==pos[y])
    {
        for(register int i=x;i<=y;i++)
            if(a[i]+tag[pos[i]]<k)sum++;
        return sum;
    }
    for(register int i=x;i<=r[pos[x]];i++)if(a[i]+tag[pos[i]]<k)sum++;
    for(register int i=l[pos[y]];i<=y;i++)if(a[i]+tag[pos[i]]<k)sum++;
    for(register int i=pos[x]+1;i<=pos[y]-1;i++)
    {
        int w=lower_bound(b+l[i],b+1+r[i],k-tag[i])-b;
        sum+=(w-l[i]);
    }
    return sum;
}
signed main()
{
    scanf("%lld",&n);
    len=sqrt(n);
    cnt=(n/len)+(n%len!=0);
    for(register int i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
        b[i]=a[i];
        pos[i]=(i-1)/len+1;
    }
    for(register int i=1;i<=cnt;i++)
    {
        l[i]=(i-1)*len+1;
        r[i]=i*len;
    }
    r[cnt]=n;
    for(register int i=1;i<=cnt;i++)
        sort(b+l[i],b+1+r[i]);
    for(register int i=1;i<=n;i++)
    {
        int op,x,y,c;
        scanf("%lld%lld%lld%lld",&op,&x,&y,&c);
        if(op==0)
        {
            add(x,y,c);
            for(register int i=l[pos[x]];i<=r[pos[x]];i++)b[i]=a[i];
            for(register int i=l[pos[y]];i<=r[pos[y]];i++)b[i]=a[i];
            sort(b+l[pos[x]],b+1+r[pos[x]]);
            sort(b+l[pos[y]],b+1+r[pos[y]]);
        }
        if(op==1)printf("%lld\n",ask(x,y,c*c));
    }
    return 0;
}

3. 数列分块入门3

区间修改,询问区间中 \(c\) 的前驱

和上一题差不多,同样也是块内排序,二分查找

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN=1e5+5;
int n,len,cnt;
int a[MAXN],b[MAXN],tag[MAXN];
int pos[MAXN],l[MAXN],r[MAXN];
inline void add(int x,int y,int k)
{
    if(x>y)return;
    if(pos[x]==pos[y])
    {
        for(register int i=x;i<=y;i++)
            a[i]+=k;
        return;
    }
    for(register int i=x;i<=r[pos[x]];i++)a[i]+=k;
    for(register int i=l[pos[y]];i<=y;i++)a[i]+=k;
    for(register int i=pos[x]+1;i<=pos[y]-1;i++)tag[i]+=k;
}
inline int ask(int x,int y,int k)
{
    int ans=-1;
    if(x>y)return ans;
    if(pos[x]==pos[y])
    {
        for(register int i=x;i<=y;i++)
            if(a[i]+tag[pos[i]]<k)ans=max(ans,a[i]+tag[pos[i]]);
        return ans;
    }
    for(register int i=x;i<=r[pos[x]];i++)if(a[i]+tag[pos[i]]<k)ans=max(ans,a[i]+tag[pos[i]]);
    for(register int i=l[pos[y]];i<=y;i++)if(a[i]+tag[pos[i]]<k)ans=max(ans,a[i]+tag[pos[i]]);
    for(register int i=pos[x]+1;i<=pos[y]-1;i++)
    {
        int w=lower_bound(b+l[i],b+1+r[i],k-tag[i])-b;
        w--;
        if(b[w]+tag[pos[w]]<k)ans=max(ans,b[w]+tag[pos[w]]);
    }
    return ans;
}
signed main()
{
    scanf("%lld",&n);
    len=sqrt(n);
    cnt=(n/len)+(n%len!=0);
    for(register int i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
        b[i]=a[i];
        pos[i]=(i-1)/len+1;
    }
    for(register int i=1;i<=cnt;i++)
    {
        l[i]=(i-1)*len+1;
        r[i]=i*len;
    }
    r[cnt]=n;
    for(register int i=1;i<=cnt;i++)
        sort(b+l[i],b+1+r[i]);
    for(register int i=1;i<=n;i++)
    {
        int op,x,y,c;
        scanf("%lld%lld%lld%lld",&op,&x,&y,&c);
        if(op==0)
        {
            add(x,y,c);
            for(register int i=l[pos[x]];i<=r[pos[x]];i++)b[i]=a[i];
            for(register int i=l[pos[y]];i<=r[pos[y]];i++)b[i]=a[i];
            sort(b+l[pos[x]],b+1+r[pos[x]]);
            sort(b+l[pos[y]],b+1+r[pos[y]]);
        }
        if(op==1)printf("%lld\n",ask(x,y,c));
    }
    return 0;
}

4. 数列分块入门4

区间修改,区间查询

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN=1e5+5;
int n,len,cnt;
int a[MAXN],tag[MAXN],t[MAXN];
int pos[MAXN],l[MAXN],r[MAXN];
inline void add(int x,int y,int k)
{
    if(x>y)return;
    if(pos[x]==pos[y])
    {
        for(register int i=x;i<=y;i++)
        {
            a[i]+=k;
            t[pos[i]]+=k;
        }
        return;
    }
    for(register int i=x;i<=r[pos[x]];i++)a[i]+=k,t[pos[i]]+=k;
    for(register int i=l[pos[y]];i<=y;i++)a[i]+=k,t[pos[i]]+=k;
    for(register int i=pos[x]+1;i<=pos[y]-1;i++)tag[i]+=k;
}
inline int ask(int x,int y,int k)
{
    int sum=0;
    if(x>y)return 0;
    if(pos[x]==pos[y])
    {
        for(register int i=x;i<=y;i++)
            sum=((sum+a[i])%k+tag[pos[i]])%k;
        return sum;
    }
    for(register int i=x;i<=r[pos[x]];i++)sum=((sum+a[i])%k+tag[pos[i]])%k;
    for(register int i=l[pos[y]];i<=y;i++)sum=((sum+a[i])%k+tag[pos[i]])%k;
    for(register int i=pos[x]+1;i<=pos[y]-1;i++)sum=((sum+t[i])%k+((r[i]-l[i]+1)%k)*tag[i]%k)%k;
    return sum;
}
signed main()
{
    scanf("%lld",&n);
    len=sqrt(n);
    cnt=(n/len)+(n%len!=0);
    for(register int i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
        pos[i]=(i-1)/len+1;
        t[pos[i]]+=a[i];
    }
    for(register int i=1;i<=cnt;i++)
    {
        l[i]=(i-1)*len+1;
        r[i]=i*len;
    }
    r[cnt]=n;
    for(register int i=1;i<=n;i++)
    {
        int op,x,y,c;
        scanf("%lld%lld%lld%lld",&op,&x,&y,&c);
        if(op==0)add(x,y,c);
        if(op==1)printf("%lld\n",ask(x,y,c+1));
    }
    return 0;
}

5. 数列分块入门5

区间开方,区间求和

记录当前块中最大值,当最大值为 \(1\) 时不需要进行开方操作

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN=1e5+5;
int n,len,cnt;
int a[MAXN],maxn[MAXN],t[MAXN];
int pos[MAXN],l[MAXN],r[MAXN];
inline void change(int x,int y)
{
    if(x>y)return;
    if(pos[x]==pos[y])
    {
        for(register int i=x;i<=y;i++)
        {
            t[pos[i]]-=a[i];
            a[i]=sqrt(a[i]);
            t[pos[i]]+=a[i];
        }
        return;
    }
    for(register int i=x;i<=r[pos[x]];i++)t[pos[i]]-=a[i],a[i]=sqrt(a[i]),t[pos[i]]+=a[i];
    for(register int i=l[pos[y]];i<=y;i++)t[pos[i]]-=a[i],a[i]=sqrt(a[i]),t[pos[i]]+=a[i];
    for(register int i=pos[x]+1;i<=pos[y]-1;i++)
    {
        if(maxn[i]==1)continue;
        maxn[i]=sqrt(maxn[i]);
        for(register int j=l[i];j<=r[i];j++)
        {
            t[i]-=a[j];
            a[j]=sqrt(a[j]);
            t[i]+=a[j];
        }
    }
}
inline int ask(int x,int y)
{
    int sum=0;
    if(x>y)return 0;
    if(pos[x]==pos[y])
    {
        for(register int i=x;i<=y;i++)
            sum+=a[i];
        return sum;
    }
    for(register int i=x;i<=r[pos[x]];i++)sum+=a[i];
    for(register int i=l[pos[y]];i<=y;i++)sum+=a[i];
    for(register int i=pos[x]+1;i<=pos[y]-1;i++)sum+=t[i];
    return sum;
}
signed main()
{
    scanf("%lld",&n);
    len=sqrt(n);
    cnt=(n/len)+(n%len!=0);
    for(register int i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
        pos[i]=(i-1)/len+1;
        t[pos[i]]+=a[i];
        maxn[pos[i]]=max(maxn[pos[i]],a[i]);
    }
    for(register int i=1;i<=cnt;i++)
    {
        l[i]=(i-1)*len+1;
        r[i]=i*len;
    }
    r[cnt]=n;
    for(register int i=1;i<=n;i++)
    {
        int op,x,y,c;
        scanf("%lld%lld%lld%lld",&op,&x,&y,&c);
        if(op==0)
        {
            change(x,y);
            if(maxn[pos[x]]!=1)
            {
                maxn[pos[x]]=0;
                for(register int i=l[pos[x]];i<=r[pos[x]];i++)maxn[pos[x]]=max(maxn[pos[x]],a[i]);
            }
            if(maxn[pos[y]]!=1)
            {
                maxn[pos[y]]=0;
                for(register int i=l[pos[y]];i<=r[pos[y]];i++)maxn[pos[y]]=max(maxn[pos[y]],a[i]);
            }
        }
        if(op==1)printf("%lld\n",ask(x,y));
    }
    return 0;
}

6. 数列分块入门6

单点插入,单点查询

因为数据随机所以不需要重构块

点击查看代码
#include<bits/stdc++.h>
#define pii pair<int,int>
#define mp make_pair
#define int long long
using namespace std;
const int MAXN=1e5+5;
int n,len,cnt;
int a[MAXN],pos[MAXN];
vector<int>v[1005];
inline pii ask(int x)
{
    int k=1;
    while(x>v[k].size())
    {
        x-=v[k].size();
        k++;
    }
    return mp(k,x-1);
}
inline void add(int x,int k)
{
    pii now=ask(x);
    v[now.first].insert(v[now.first].begin()+now.second,k);
}
signed main()
{
    scanf("%lld",&n);
    len=sqrt(n);
    cnt=(n/len)+(n%len!=0);
    for(register int i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
        pos[i]=(i-1)/len+1;
        v[pos[i]].push_back(a[i]);
    }
    for(register int i=1;i<=n;i++)
    {
        int op,x,y,c;
        scanf("%lld%lld%lld%lld",&op,&x,&y,&c);
        if(op==0)add(x,y);
        if(op==1)
        {
            pii now=ask(y);
            printf("%lld\n",v[now.first][now.second]);
        }
    }
    return 0;
}

7. 数列分块入门7

区间加乘,单点查询

重点是边上的散块,加或乘的时候直接将所有标记下传即可

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN=1e5+5;
const int MOD=10007;
int n,len,cnt;
int a[MAXN],tag[MAXN],mul[MAXN];
int pos[MAXN],l[MAXN],r[MAXN];
inline void pushdown(int p)
{
    for(register int i=l[p];i<=r[p];i++)
        a[i]=(a[i]*mul[p]%MOD+tag[p])%MOD;
    mul[p]=1;
    tag[p]=0;
}
inline void add(int x,int y,int k)
{
    if(x>y)return;
    if(pos[x]==pos[y])
    {
        pushdown(pos[x]);
        for(register int i=x;i<=y;i++)
            a[i]=(a[i]+k)%MOD;
        return;
    }
    pushdown(pos[x]);
    pushdown(pos[y]);
    for(register int i=x;i<=r[pos[x]];i++)a[i]=(a[i]+k)%MOD;
    for(register int i=l[pos[y]];i<=y;i++)a[i]=(a[i]+k)%MOD;
    for(register int i=pos[x]+1;i<=pos[y]-1;i++)tag[i]=(tag[i]+k)%MOD;
}
inline void mult(int x,int y,int k)
{
    if(x>y)return;
    if(pos[x]==pos[y])
    {
        pushdown(pos[x]);
        for(register int i=x;i<=y;i++)
            a[i]=(a[i]*k)%MOD;
        return;
    }
    pushdown(pos[x]);
    pushdown(pos[y]);
    for(register int i=x;i<=r[pos[x]];i++)a[i]=(a[i]*k)%MOD;
    for(register int i=l[pos[y]];i<=y;i++)a[i]=(a[i]*k)%MOD;
    for(register int i=pos[x]+1;i<=pos[y]-1;i++)tag[i]=(tag[i]*k)%MOD,mul[i]=(mul[i]*k)%MOD;
}
signed main()
{
    scanf("%lld",&n);
    len=sqrt(n);
    cnt=(n/len)+(n%len!=0);
    for(register int i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
        pos[i]=(i-1)/len+1;
    }
    for(register int i=1;i<=cnt;i++)
    {
        mul[i]=1,tag[i]=0; 
        l[i]=(i-1)*len+1;
        r[i]=i*len;
    }
    r[cnt]=n;
    for(register int i=1;i<=n;i++)
    {
        int op,x,y,c;
        scanf("%lld%lld%lld%lld",&op,&x,&y,&c);
        if(op==0)add(x,y,c);
        if(op==1)mult(x,y,c);
        if(op==2)printf("%lld\n",(a[y]*mul[pos[y]]%MOD+tag[pos[y]])%MOD);
    }
    return 0;
}

8. 数列分块入门8

区间求特定数的个数+覆盖

可以记录一个当前块中的元素是否相等的标记,如果作为散块被访问的话就先下传标记。

点击查看代码
#include<bits/stdc++.h>
#define int long long
#define INF 0x7f7f7f7f7f7f7f7f
using namespace std;
const int MAXN=1e5+5;
int n,len,cnt;
int a[MAXN],flag[MAXN];
int pos[MAXN],l[MAXN],r[MAXN];
inline void pushdown(int k)
{
    if(flag[k]==INF)return;
    for(register int i=l[k];i<=r[k];i++)
        a[i]=flag[k];
    flag[k]=INF;
}
inline int change(int x,int y,int k)
{
    int sum=0;
    if(x>y)return 0;
    if(pos[x]==pos[y])
    {
        pushdown(pos[x]);
        for(register int i=x;i<=y;i++)
        {
            if(a[i]!=k)a[i]=k;
            else sum++;
        }
        return sum;
    }
    pushdown(pos[x]);
    pushdown(pos[y]);
    for(register int i=x;i<=r[pos[x]];i++)
    {
        if(a[i]!=k)a[i]=k;
        else sum++;
    }
    for(register int i=l[pos[y]];i<=y;i++)
    {
        if(a[i]!=k)a[i]=k;
        else sum++;
    }
    for(register int i=pos[x]+1;i<=pos[y]-1;i++)
    {
        if(flag[i]==k)sum+=(r[i]-l[i]+1);
        else if(flag[i]!=INF)flag[i]=k;
        else
        {
            flag[i]=k;
            for(register int j=l[i];j<=r[i];j++)
            {
                if(a[j]==k)sum++;
                else a[j]=k;
            }
        }
    }
    return sum;
}
signed main()
{
    scanf("%lld",&n);
    len=sqrt(n);
    cnt=(n/len)+(n%len!=0);
    for(register int i=1;i<=n;i++)
    {
        scanf("%lld",&a[i]);
        pos[i]=(i-1)/len+1;

    }
    for(register int i=1;i<=cnt;i++)
    {
        l[i]=(i-1)*len+1;
        r[i]=i*len;
        for(register int j=l[i]+1;j<=r[i];j++)
            if(a[j]!=a[j-1])
            {
                flag[i]=INF;
                break;
            }
        if(flag[i]!=INF)flag[i]=a[l[i]];
    }
    r[cnt]=n;
    for(register int i=1;i<=n;i++)
    {
        int x,y,c;
        scanf("%lld%lld%lld",&x,&y,&c);
        printf("%lld\n",change(x,y,c));
    }
    return 0;
}
posted @ 2023-07-04 16:58  yzh_Error404  阅读(14)  评论(0)    收藏  举报