分块算法初步

题意:

1.给出一个长为  的数列,以及  个操作,操作涉及区间加法,单点查值。

2.给出一个长为  的数列,以及  个操作,操作涉及区间加法,询问区间内小于某个值  的元素个数。

3.给出一个长为 nn 的数列,以及 nn 个操作,操作涉及区间加法,询问区间内小于某个值 xx 的前驱(比其小的最大元素)。

4.给出一个长为 nn 的数列,以及 nn 个操作,操作涉及区间加法,区间求和,mod(c+1)。

 

原题地址:

https://loj.ac/problem/6277

https://loj.ac/problem/6278

https://loj.ac/problem/6279

https://loj.ac/problem/6280

 

参考博客:https://www.cnblogs.com/Parsnip/p/10458689.html#

 

代码:

#include<bits/stdc++.h>

#define closeSync ios::sync_with_stdio(0);cin.tie(0);cout.tie(0)
#define multiCase int T;cin>>T;for(int t=1;t<=T;t++)
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define repp(i,a,b) for(int i=a;i<b;i++)
#define per(i,a,b) for(int i=a;i>=b;i--)
#define perr(i,a,b) for(int i=a;i>b;i--)
#define pb push_back
#define eb push_back
#define mst(a,b) memset(a,b,sizeof(a))
using namespace std;

typedef long long ll;

template <typename _Tp> inline _Tp read(_Tp&x){
    char c11=getchar(),ob=0;x=0;
    while(c11^'-'&&!isdigit(c11))c11=getchar();if(c11=='-')c11=getchar(),ob=1;
    while(isdigit(c11))x=x*10+c11-'0',c11=getchar();if(ob)x=-x;return x;
}


const int N=1000010,sqrtN=1000;
int n,t,L[sqrtN],R[sqrtN],pos[N];//L[j]、R[j]分别表示第j个块所管辖区间的左边界和右边界,pos[i]表示序列中第i个元素属于哪个块
ll a[N],changed[sqrtN],sum[sqrtN];//a数组用来存序列元素,changed数组用来表示每个块中每个元素共同的变化量,sum数组用来存每个块中元素的和
vector<int>section[N];//用于管理每个块中的元素
//输入元素
inline void input()
{
    scanf("%d",&n);
    rep(i,1,n)scanf("%d",&a[i]);
}
//将对应元素存入对应的块
inline void reset(int x)
{
    section[x].clear();
    rep(i,L[x],R[x])section[x].pb(a[i]);
    sort(section[x].begin(),section[x].end());
}
//预处理,分为sqrt(n)或sqrt(n)+1个块
inline void init()
{
    t=sqrt(n);
    rep(i,1,t)
    {
        L[i]=(i-1)*t+1;
        R[i]=i*t;
    }
    if(R[t]<n)t++,L[t]=R[t-1]+1,R[t]=n;
    rep(i,1,t)
    rep(j,L[i],R[i])
    pos[j]=i,sum[i]+=a[j];
    rep(i,1,t)reset(i);
}
//区间修改,这里是整体加data
inline void change(int l,int r,ll data)
{
    int p=pos[l],q=pos[r];
    if(q==p)
    {
        rep(i,l,r)a[i]+=data;
        sum[p]+=(r-l+1)*data;
        reset(p);
    }
    else
    {
        rep(i,p+1,q-1)changed[i]+=data;
        rep(i,l,R[p])a[i]+=data;
        sum[p]+=(R[p]-l+1)*data;
        reset(p);
        rep(i,L[q],r)a[i]+=data;
        sum[q]+=(r-L[q]+1)*data;
        reset(q);
    }
}
//用于更新区间内小于某个值 x 的前驱
inline void updata(ll &ans2,ll val,ll limit)
{
    if(val<limit&&val>ans2)ans2=val;
}
//查询a[x]的值
inline ll ask1(int x)
{
    return a[x]+changed[pos[x]];
}
//查询区间内小于某个值limit的元素个数
inline int ask2(int l,int r,ll limit)
{
    int p=pos[l],q=pos[r],ans1=0;
    if(p==q)
    {
        rep(i,l,r)
        if(a[i]+changed[p]<limit)ans1++;
    }
    else
    {
        rep(i,p+1,q-1)ans1+=lower_bound(section[i].begin(),section[i].end(),limit-changed[i])-section[i].begin();
        rep(i,l,R[p])if(a[i]+changed[p]<limit)ans1++;
        rep(i,L[q],r)if(a[i]+changed[q]<limit)ans1++;
    }
    return ans1;
}
//询问区间内小于某个值 x 的前驱(比其小的最大元素)
inline ll ask3(int l,int r,ll limit)
{
    int p=pos[l],q=pos[r];
    ll ans2=-1;
    if(p==q)
    {
        rep(i,l,r)
        updata(ans2,a[i]+changed[p],limit);
    }
    else
    {
        rep(i,p+1,q-1)
        updata(ans2,section[i][lower_bound(section[i].begin(),section[i].end(),limit-changed[i])-section[i].begin()-1]+changed[i],limit);
        rep(i,l,R[p])
        updata(ans2,a[i]+changed[pos[i]],limit);
        rep(i,L[q],r)
        updata(ans2,a[i]+changed[pos[i]],limit);
    }
    return ans2;
}
//区间求和取模
inline ll ask4(int l,int r,ll MOD)
{
    int p=pos[l],q=pos[r];
    ll ans3=0;
    if(p==q)
    {
        rep(i,l,r)ans3=(ans3+a[i])%MOD;
        ans3=(ans3+(r-l+1)*changed[p])%MOD;
    }
    else
    {
        rep(i,p+1,q-1)ans3=(ans3+sum[i]%MOD+(R[i]-L[i]+1)*changed[i])%MOD;
        rep(i,l,R[p])ans3=(ans3+a[i]+changed[pos[i]])%MOD;
        rep(i,L[q],r)ans3=(ans3+a[i]+changed[pos[i]])%MOD;
    }
    return ans3;
}
inline void solve()
{
    input();
    init();
    int opt,l,r;ll c;
    rep(i,1,n)
    {
        scanf("%d%d%d%lld",&opt,&l,&r,&c);
        if(opt)printf("%lld\n",ask4(l,r,c+1));
        else change(l,r,c);
    }
}
int main()
{
    solve();
    return 0;
}

 

posted @ 2020-08-12 20:31  chuliyou  阅读(146)  评论(0)    收藏  举报