分块 总结

分块算法:

b l o c k = n block=\sqrt n block=n 表示块大小,用两个数组表示每块的头和尾,第i个位置表示那个位置用 ( i − 1 ) / b l o c k + 1 (i-1)/block+1 (i1)/block+1

int blck = sqrt(n);
int t = n/block;
if(n%block) t++;
for(int i=1;i<=t;i++){
  st[i] = (i-1)*block+1;
  ed[i] = i*block;
}
ed[t]=n;///最后一个是n
for(int i=1;i<=n;i++){
  pos[i]=(i-1)/block+1;
}

辅助数组:

  1. a [ i ] a[i] a[i]数组表示i位置的元素

  2. s u m [ i ] sum[i] sum[i]表示第i块的区间和预处理初值

for(int i=1;i<=t;i++){
  for(int j=st[i];j<=ed[i];j++){
    sum[i]+=a[j];
  }
}
  1. a d d [ i ] add[i] add[i]表示第i块的增减量。表示整个块都需要增加。

对[l,r]区间进行+d操作,两种选择:

  1. 如果是对一个整块那么我们对add[i]进行+d,sum 不变

  2. 如果不是整块,那么我们对sum数组进行操作。

区间修改

void change(int l,int r,int d){///分块是优雅的暴力
  int p = pos[l],q=pos[r];
  if(p==q){///属于同一块
    for(int i=li<=r;i++) a[i]+=d;
    sum[p]+=d*(r-l+1);    
  }else {///不属于同一块
    for(int i=p+1;i<q;i++)add[i]+=d;///整块操作
    for(int i=l;i<=ed[p];i++)a[i]+=d;///对左端进行操作
    sum[p]+=d*(ed[p]-l);
    for(int i=st[q];i<=r;i++) a[i]+=d;///对右端进行操作
    sum[q]+=d*(r-st[q]);
  }
}

区间查询

两种情况:

  1. [l,r]在同一个块中,那么暴力加上[l,r]这些数,最后再加上(r-l+1)个add[i]。答案是 a n s = a [ l ] + a [ l + 1 ] + . . . . + a [ r ] + ( r − l + 1 ) ∗ a d d [ l ] ans=a[l]+a[l+1]+....+a[r]+(r-l+1)*add[l] ans=a[l]+a[l+1]+....+a[r]+(rl+1)add[l]

  2. [l,r]中间有其他块,那么中间块进行枚举,每个块的贡献是sum[i]+add[i]*block,对两端进行操作1。

ll ask(int l,int r){
  int p=pos[l],q=pos[r];
  ll ans=0;
  if(p==q){
    for(int i=l;i<=r;i++) ans+=a[i];
    ans+=add[p]*(r-l+1);
   }else {
     for(int i=p+1;i<q;i++)ans+=sum[i]+add[i]*block;
     for(int i=l;i<=ed[p];i++) ans+=a[i];
     ans+=add[p]*(ed[p]-l+1);
     for(int i=st[q];i<=r;i++) ans+=a[i];
     ans+=add[q]*(r-st[q]+1);
   }

复杂度分析:

每一次查询或者修改复杂度最大是 2 n \sqrt {2n} 2n ,一共操作 m m m次复杂度是 m n m\sqrt n mn

练习题:

我们用分块来做,需要二分,那么就需要维护一个单调的序列。但是又不能破坏原本的序列,那么我们用一个新序列维护他的单调性即可。

#include <bits/stdc++.h>
using namespace std;

const int maxn = 1e6 + 7;

int n,m;
int a[maxn],b[maxn],pos[maxn];
int st[maxn],ed[maxn],sum[maxn],add[maxn];
void fr(char &x)
{
    x=0;while(x!='M'&&x!='A') x=getchar();
}
void build(){
    int block=sqrt(n);
    int t = n/block;
    if(n%block) t++;
    for(int i=1;i<=t;i++){
        st[i]=(i-1)*block+1;
        ed[i]=i*block;
    }
    ed[t]=n;
    for(int i=1;i<=n;i++){
        pos[i]=(i-1)/block+1;
        b[i]=a[i];
    }
    for(int i=1;i<=t;i++){
        sort(b+st[i],b+ed[i]+1);
    }
}
void change(int l,int r,int d){
    int p=pos[l],q=pos[r];
    if(p==q){
        for(int i=l;i<=r;i++){
            a[i]+=d;
        }
        for(int i=st[q];i<=ed[q];i++){
            b[i]=a[i];
        }
        sort(b+st[q],b+ed[q]+1);
    }else {
        for(int i=l;i<=ed[p];i++) a[i]+=d;
        for(int i=st[p];i<=ed[p];i++) b[i]=a[i];
        sort(b+st[p],b+ed[p]+1);
        for(int i=st[q];i<=r;i++) a[i]+=d;
        for(int i=st[q];i<=ed[q];i++) b[i]=a[i];
        sort(b+st[q],b+ed[q]+1);

        for(int i=p+1;i<q;i++) add[i]+=d;
    }
}
int query(int l,int r,int d){
    int ans=0;
    int p=pos[l],q=pos[r];
    if(p==q) {
        for(int i=l;i<=r;i++){
            if(a[i]+add[p]>=d) ans++;
        }
    }else {
        for(int i=l;i<=ed[p];i++) if(a[i]+add[p]>=d) ans++;
        for(int i=st[q];i<=r;i++) if(a[i]+add[q]>=d) ans++;
        for(int i=p+1;i<q;i++){
            int lx=st[i],rx=ed[i];
            int sum=0;
            while(lx<=rx){
                int mid=(lx+rx)/2;
                if(b[mid]+add[i]>=d){
                    rx=mid-1;
                    sum=ed[i]-mid+1;
                }else {
                    lx=mid+1;
                }
            }
            ans+=sum;
        }        
    }
    return ans;
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
    }
    build();
    while(m--){
        char ch;
        fr(ch);
        int l,r,d;
        scanf("%d%d%d",&l,&r,&d);
        if(ch=='A'){
            printf("%d\n",query(l,r,d));
        }else {
            change(l,r,d);
        }
    }
    return 0;
}








数列分块入门1

区间加法+单点查询:前段时间学的树状数组,我直接用树状数组维护的

#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e5 + 7;
#define ll long long
#define x first
#define y second
#define pii pair<int ,int >
const int mod = 1e9 + 7;

ll qpow(ll a, ll b, ll p)
{
    ll ans = 1;
    while (b)
    {
        if(b & 1) ans = ans * a % p;
        b >>= 1;
        a = a * a % p;
    }
    return ans;
}


ll tree[maxn];

ll lowbit(ll x){
    return x&(-x);
}
void update(ll pos,ll x){
    while(pos<maxn){
        tree[pos]+=x;
        pos+=lowbit(pos);
    }
}
ll query(ll pos){
    ll ans=0;
    while(pos>0){
        ans+=tree[pos];
        pos-=lowbit(pos);
    }
    return ans;
}


void solve()
{
    ll n;
    scanf("%lld",&n);
    for(int i=1;i<=n;i++){
        ll x;
        scanf("%lld",&x);
        update(i,x);
        update(i+1,-x);
    }
    for(int i=1;i<=n;i++){
        ll op,l,r,c;
        scanf("%lld%lld%lld%lld",&op,&l,&r,&c);
        if(op==0){
            update(l,c);
            update(r+1,-c);
        }else {
            printf("%lld\n",query(r));
        }
    }
}

int main()
{
    int t = 1;
    //scanf("%d", &t);
    while(t--)
    {
        solve();
    }
    return 0;
}

数列分块入门 2

区间加法+区间查询小于x的个数

#include <bits/stdc++.h>
using namespace std;
#define ll long long 
const int maxn = 2e5 + 7;

int n,m;
ll a[maxn],b[maxn],pos[maxn];
ll st[maxn],ed[maxn],sum[maxn],add[maxn];
void fr(char &x)
{
    x=0;while(x!='0'&&x!='1') x=getchar();
}
void build(){
    ll block=sqrt(n);
    ll t = n/block;
    if(n%block) t++;
    for(int i=1;i<=t;i++){
        st[i]=(i-1)*block+1;
        ed[i]=i*block;
    }
    ed[t]=n;
    for(int i=1;i<=n;i++){
        pos[i]=(i-1)/block+1;
        b[i]=a[i];
    }
    for(int i=1;i<=t;i++){
        sort(b+st[i],b+ed[i]+1);
    }
}
void change(ll l,ll r,ll d){
    ll p=pos[l],q=pos[r];
    if(p==q){
        for(int i=l;i<=r;i++){
            a[i]+=d;
        }
        for(int i=st[q];i<=ed[q];i++){
            b[i]=a[i];
        }
        sort(b+st[q],b+ed[q]+1);
    }else {
        for(int i=l;i<=ed[p];i++) a[i]+=d;
        for(int i=st[p];i<=ed[p];i++) b[i]=a[i];
        sort(b+st[p],b+ed[p]+1);
        for(int i=st[q];i<=r;i++) a[i]+=d;
        for(int i=st[q];i<=ed[q];i++) b[i]=a[i];
        sort(b+st[q],b+ed[q]+1);

        for(int i=p+1;i<q;i++) add[i]+=d;
    }
}
ll query(ll l,ll r,ll d){
    ll ans=0;
    ll p=pos[l],q=pos[r];
    if(p==q) {
        for(int i=l;i<=r;i++){
            if(a[i]+add[p]<d) ans++;
        }
    }else {
        for(int i=l;i<=ed[p];i++) if(a[i]+add[p]<d) ans++;
        for(int i=st[q];i<=r;i++) if(a[i]+add[q]<d) ans++;
        for(int i=p+1;i<q;i++){
            ll lx=st[i],rx=ed[i];
            ll sum=0;
            while(lx<=rx){
                ll mid=(lx+rx)/2;
                if(b[mid]+add[i]<d){
                    lx=mid+1;
                    sum=mid-st[i]+1;
                }else {
                    rx=mid-1;
                }
            }
            ans+=sum;
        }        
    }
    return ans;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%lld",&a[i]);
    }
    build();
    m=n;
    while(m--){
        char ch;
        fr(ch);
        ll l,r,d;
        scanf("%lld%lld%lld",&l,&r,&d);
        if(ch=='1'){
            printf("%lld\n",query(l,r,d*d));
        }else {
            change(l,r,d);
        }
    }
    return 0;
}

数列分块入门 3

区间加法+区间找小于x的最大值

#include <bits/stdc++.h>
using namespace std;
#define ll long long 
const int maxn = 2e5 + 7;

int n,m;
ll a[maxn],b[maxn],pos[maxn];
ll st[maxn],ed[maxn],sum[maxn],add[maxn];
void fr(char &x)
{
    x=0;while(x!='0'&&x!='1') x=getchar();
}
void build(){
    ll block=sqrt(n);
    ll t = n/block;
    if(n%block) t++;
    for(int i=1;i<=t;i++){
        st[i]=(i-1)*block+1;
        ed[i]=i*block;
    }
    ed[t]=n;
    for(int i=1;i<=n;i++){
        pos[i]=(i-1)/block+1;
        b[i]=a[i];
    }
    for(int i=1;i<=t;i++){
        sort(b+st[i],b+ed[i]+1);
    }
}
void change(ll l,ll r,ll d){
    ll p=pos[l],q=pos[r];
    if(p==q){
        for(int i=l;i<=r;i++){
            a[i]+=d;
        }
        for(int i=st[q];i<=ed[q];i++){
            b[i]=a[i];
        }
        sort(b+st[q],b+ed[q]+1);
    }else {
        for(int i=l;i<=ed[p];i++) a[i]+=d;
        for(int i=st[p];i<=ed[p];i++) b[i]=a[i];
        sort(b+st[p],b+ed[p]+1);
        for(int i=st[q];i<=r;i++) a[i]+=d;
        for(int i=st[q];i<=ed[q];i++) b[i]=a[i];
        sort(b+st[q],b+ed[q]+1);

        for(int i=p+1;i<q;i++) add[i]+=d;
    }
}
ll query(ll l,ll r,ll d){
    ll ans=-1;
    ll p=pos[l],q=pos[r];
    if(p==q) {
        for(int i=l;i<=r;i++){
            if(a[i]+add[p]<d) ans=max(ans,a[i]+add[p]);
        }
    }else {
        for(int i=l;i<=ed[p];i++) if(a[i]+add[p]<d) ans=max(ans,a[i]+add[p]);
        for(int i=st[q];i<=r;i++) if(a[i]+add[q]<d) ans=max(ans,a[i]+add[q]);
        for(int i=p+1;i<q;i++){
            ll lx=st[i],rx=ed[i];
            ll sum=0;
            while(lx<=rx){
                ll mid=(lx+rx)/2;
                if(b[mid]+add[i]<d){
                    lx=mid+1;
                    sum=mid;
                }else {
                    rx=mid-1;
                }
            }
            if(sum&&b[sum]+add[i]<d) ans=max(ans,b[sum]+add[i]);
        }        
    }
    return ans;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%lld",&a[i]);
    }
    build();
    m=n;
    while(m--){
        char ch;
        fr(ch);
        ll l,r,d;
        scanf("%lld%lld%lld",&l,&r,&d);
        if(ch=='1'){
            printf("%lld\n",query(l,r,d));
        }else {
            change(l,r,d);
        }
    }
    return 0;
}

数列分块入门 4

区间加法+区间求和

#include <bits/stdc++.h>
using namespace std;
#define ll long long 
const int maxn = 2e5 + 7;

int n,m;
ll a[maxn],pos[maxn];
ll st[maxn],ed[maxn],sum[maxn],add[maxn];
void fr(char &x)
{
    x=0;while(x!='0'&&x!='1') x=getchar();
}
void build(){
    ll block=sqrt(n);
    ll t = n/block;
    if(n%block) t++;
    for(int i=1;i<=t;i++){
        st[i]=(i-1)*block+1;
        ed[i]=i*block;
    }
    ed[t]=n;
    for(int i=1;i<=n;i++){
        pos[i]=(i-1)/block+1;
    }
    for(int i=1;i<=t;i++){
    for(int j=st[i];j<=ed[i];j++){
      sum[i]+=a[j];
    }
    }
}
void change(ll l,ll r,ll d){
    ll p=pos[l],q=pos[r];
    if(p==q){
        for(int i=l;i<=r;i++){
            a[i]+=d;
        }
        sum[p]+=(r-l+1)*d;
    }else {
        for(int i=l;i<=ed[p];i++) a[i]+=d;
        sum[p]+=d*(ed[p]-l+1);
        for(int i=st[q];i<=r;i++) a[i]+=d;
        sum[q]+=d*(r-st[q]+1);
        for(int i=p+1;i<q;i++){
          add[i]+=d;
        }
    }
}
ll query(ll l,ll r,ll d){
    ll ans=0;
    ll p=pos[l],q=pos[r];
    if(p==q) {
        for(int i=l;i<=r;i++){
            ans=(ans+a[i]+d+1)%(d+1);
        }
        ans+=add[p]*(r-l+1)%(d+1);
        ans%=(d+1);
    }else {
        for(int i=l;i<=ed[p];i++) ans=(ans+a[i]+d+1)%(d+1);
        ans+=add[p]*(ed[p]-l+1)%(d+1);
        ans%=(d+1);
        for(int i=st[q];i<=r;i++) ans=(ans+a[i]+d+1)%(d+1);
        ans+=add[q]*(r-st[q]+1)%(d+1);
        ans%=(d+1);
        for(int i=p+1;i<q;i++) ans=((ans+sum[i]+d+1)%(d+1)+add[i]*(ed[i]-st[i]+1)%(d+1)+(d+1))%(d+1);
    }
    return ans;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%lld",&a[i]);
    }
    build();
    m=n;
    while(m--){
        char ch;
        fr(ch);
        ll l,r,d;
        scanf("%lld%lld%lld",&l,&r,&d);
        if(ch=='1'){
            printf("%lld\n",query(l,r,d));
        }else {
            change(l,r,d);
        }
    }
    return 0;
}

数列分块入门 5

区间开方,区间求和。

#include <bits/stdc++.h>
using namespace std;
#define ll long long 
const int maxn = 2e5 + 7;

int n,m;
ll a[maxn],pos[maxn];
ll st[maxn],ed[maxn],sum[maxn],add[maxn];
void fr(char &x)
{
    x=0;while(x!='0'&&x!='1') x=getchar();
}
void build(){
    ll block=sqrt(n);
    ll t = n/block;
    if(n%block) t++;
    for(int i=1;i<=t;i++){
        st[i]=(i-1)*block+1;
        ed[i]=i*block;
    }
    ed[t]=n;
    for(int i=1;i<=n;i++){
        pos[i]=(i-1)/block+1;
    }
    for(int i=1;i<=t;i++){
    for(int j=st[i];j<=ed[i];j++){
      sum[i]+=a[j];
    }
    }
}
void change(ll l,ll r,ll d){
    ll p=pos[l],q=pos[r];
    if(p==q){
        for(int i=l;i<=r;i++){
            sum[p]-=a[i];
            a[i]=sqrt(a[i]);
            sum[p]+=a[i];
        }
    }else {
        for(int i=l;i<=ed[p];i++) {
          sum[p]-=a[i];
            a[i]=sqrt(a[i]);
            sum[p]+=a[i];
        }
        for(int i=st[q];i<=r;i++){
          sum[q]-=a[i];
            a[i]=sqrt(a[i]);
            sum[q]+=a[i];
        }
        
        for(int i=p+1;i<q;i++){
      if(add[i]==1) continue;
      else {
        bool flag=0;
        for(int j=st[i];j<=ed[i];j++){
          sum[i]-=a[j];
                a[j]=sqrt(a[j]);
                sum[i]+=a[j];
                if(a[j]>1) flag=1;
        }
        if(!flag) add[i]=1;
      }
        }
    }
}
ll query(ll l,ll r,ll d){
    ll ans=0;
    ll p=pos[l],q=pos[r];
    if(p==q) {
        for(int i=l;i<=r;i++){
            ans+=a[i];
        }
    }else {
         for(int i=l;i<=ed[p];i++)ans+=a[i];
         for(int i=st[q];i<=r;i++)ans+=a[i];
         for(int i=p+1;i<q;i++) ans+=sum[i];
    }
    return ans;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%lld",&a[i]);
    }
    build();
    m=n;
    while(m--){
        char ch;
        fr(ch);
        ll l,r,d;
        scanf("%lld%lld%lld",&l,&r,&d);
        if(ch=='1'){
            printf("%lld\n",query(l,r,d));
        }else {
            change(l,r,d);
        }
    }
    return 0;
}

数列分块入门 6

单点插入+单点询问

我们利vector进行分块储存,每次当一个vector种的元素数量大于20block重新对块进行分配。

#include <bits/stdc++.h>
using namespace std;
#define ll long long 
const int maxn = 2e5 + 7;

int n,m;
ll a[maxn],pos[maxn];
ll st[maxn],ed[maxn];
vector<ll> v[maxn];

ll block,t;
void fr(char &x)
{
    x=0;while(x!='0'&&x!='1') x=getchar();
}

void build(){
  ll top=0;
  for(int i=1;i<=m;++i){
    for(int j=0;j<(ll)v[i].size();j++){
      st[++top]=v[i][j];
    }
    v[i].clear();
  }
  int block2=sqrt(top);
  for(int i=1;i<=top;i++){
    int x=(i-1)/block2+1;
    v[x].push_back(st[i]);
  }
  m=(top-1)/block+1;
}

void change(ll l,ll r,ll d){
    ll now=1;
    while(l>(ll)v[now].size()){
      l-=(ll)v[now].size();
      now++;
    }
    l--;
    v[now].insert(v[now].begin()+l,r);
    if((ll)v[now].size()>20*block) build();
}

int main()
{
    scanf("%d",&n);
    block=sqrt(n);
    for(int i=1;i<=n;i++){
        scanf("%lld",&a[i]);
        v[(i-1)/block+1].push_back(a[i]);
    }
    m=(n-1)/block+1;


    while(n--){
        char ch;
        fr(ch);
        ll l,r,d;
        scanf("%lld%lld%lld",&l,&r,&d);
        if(ch=='1'){
           ll now=1;
        while(r>(ll)v[now].size()){
          r-=(ll)v[now].size();
          now++;
        }
        r--;
        printf("%lld\n",v[now][r]);
        }else {
            change(l,r,d);
        }
    }
    return 0;
}

数列分块入门 7

区间乘法+区间加法+单点查询。

我们用过分块2可知维护区间加法,那么同时怎么维护区间加法和区间乘法那?

首先我们知道 ( a + b ) ∗ c = a ∗ c + b ∗ c (a+b)*c=a*c+b*c (a+b)c=ac+bc ( a ∗ b ) + c (a*b)+c (ab)+c

知道这个之后我们就知道优先级了,知道优先级之后就直接用维护区间加法的方式维护即可,就是计算顺序注意。

#include <bits/stdc++.h>
using namespace std;
#define ll long long 
const int maxn = 2e5 + 7;
const int mod=10007;
int n,m;
ll a[maxn],pos[maxn];
ll st[maxn],ed[maxn];
ll add[maxn],mul[maxn];

ll block,t;
void fr(char &x)
{
    x=0;while(x!='0'&&x!='1'&&x!='2') x=getchar();
}

void Add(ll l,ll r,ll d){
  ll p=pos[l],q=pos[r];
  if(p==q){
    for(int i=st[p];i<=ed[p];i++){
      a[i]=(a[i]*mul[p]%mod+add[p]+mod)%mod;
      if(i>=l&&i<=r) a[i]=(a[i]+d+mod)%mod;
    }
  }
  else {
    for(int i=st[p];i<=ed[p];i++){
      a[i]=(a[i]*mul[p]%mod+add[p]+mod)%mod;
      if(i>=l&&i<=r) a[i]=(a[i]+d+mod)%mod;
    }
    for(int i=st[q];i<=ed[q];i++){
      a[i]=(a[i]*mul[q]%mod+add[q]+mod)%mod;
      if(i>=l&&i<=r) a[i]=(a[i]+d+mod)%mod;
    }
    for(int i=p+1;i<q;++i){
      add[i]=(add[i]+d+mod)%mod;
    }
  }
  mul[p]=1;
  add[p]=0;
  mul[q]=1;
  add[q]=0;
}

void Mul(ll l,ll r,ll d){
  ll p=pos[l],q=pos[r];
  if(p==q){
    for(int i=st[p];i<=ed[p];i++){
      a[i]=(a[i]*mul[p]%mod+add[p])%mod;
      if(i>=l&&i<=r) a[i]=(a[i]*d+mod)%mod;
    }
  }else {
    for(int i=st[p];i<=ed[p];i++){
      a[i]=(a[i]*mul[p]%mod+add[p])%mod;
      if(i>=l&&i<=r) a[i]=(a[i]*d+mod)%mod;
    }
    for(int i=st[q];i<=ed[q];i++){
      a[i]=(a[i]*mul[q]%mod+add[q])%mod;
      if(i>=l&&i<=r) a[i]=(a[i]*d+mod)%mod;
    }
    for(int i=p+1;i<q;++i){
      mul[i]=(mul[i]*d+mod)%mod;
      add[i]=(add[i]*d+mod)%mod;
    }
  }
  mul[p]=1;
  add[p]=0;
  mul[q]=1;
  add[q]=0;
}

int main()
{
    scanf("%d",&n);
    block=sqrt(n);
    for(int i=1;i<=n;i++){
        scanf("%lld",&a[i]);
       a[i]%=mod;
       pos[i]= (i-1)/block+1;
       add[pos[i]]=0;
       mul[pos[i]]=1;
    }
    for(int i=1;i<=pos[n];i++){
        st[i]=(i-1)*block+1;
        ed[i]=i*block;
    }
    ed[pos[n]]=n;
    
    while(n--){
        char ch;
        fr(ch);
        ll l,r,d;
        scanf("%lld%lld%lld",&l,&r,&d);
        d=d%mod;
        if(ch=='0'){
           Add(l,r,d);
        }else if(ch=='1'){
      Mul(l,r,d);
        }else {
          printf("%lld\n",(a[r]*mul[pos[r]]+add[pos[r]])%mod);
        }
    }
    return 0;
}

数列分块入门 8

区间查询c区间修改C

#include <bits/stdc++.h>
using namespace std;
#define ll long long 
const int maxn = 2e5 + 7;

int n,m;
ll a[maxn],pos[maxn];
ll st[maxn],ed[maxn],sum[maxn],add[maxn];
ll dis[maxn],vis[maxn];
void fr(char &x)
{
    x=0;while(x!='0'&&x!='1') x=getchar();
}
void build(){
    ll block=sqrt(n);
    ll t = n/block;
    if(n%block) t++;
    for(int i=1;i<=t;i++){
        st[i]=(i-1)*block+1;
        ed[i]=i*block;
        dis[i]=1;
        vis[i]=-1;
    }
    ed[t]=n;
    for(int i=1;i<=n;i++){
        pos[i]=(i-1)/block+1;
    }
}
void change(ll l,ll r,ll d){
    ll p=pos[l],q=pos[r];
    if(p==q){
      if(dis[p]==vis[p]){
        for(int i=st[p];i<=ed[p];i++){
          a[i]=dis[p];
        }
      }      
        for(int i=l;i<=r;i++){
            a[i]=d;
        }
        dis[p]=1;
        vis[p]=-1;
    }else {
      if(dis[p]==vis[p]){
        for(int i=st[p];i<=ed[p];i++){
          a[i]=dis[p];
        }
      }
        for(int i=l;i<=ed[p];i++) {
            a[i]=d;
        }
        
        if(dis[q]==vis[q]){
        for(int i=st[q];i<=ed[q];i++){
          a[i]=dis[q];
        }
      }
        for(int i=st[q];i<=r;i++){
           a[i]=d;
        }
        dis[p]=1;
        vis[p]=-1;
        dis[q]=1;
        vis[q]=-1;        
        for(int i=p+1;i<q;i++){          
          dis[i]=vis[i]=d;
        }
    }
}
ll query(ll l,ll r,ll d){
    ll ans=0;
    ll p=pos[l],q=pos[r];
    if(p==q) {
      if(dis[p]==vis[p]){
        if(dis[p]==d)
          return r-l+1;
        else return 0;
      }
        for(int i=l;i<=r;i++){
            if(a[i]==d) ans++;
        }
        return ans;
    }else {
      if(dis[p]==vis[p]){
        if(dis[p]==d)
          ans+=ed[p]-l+1;
        else ans+=0;
      }
      else {
          for(int i=l;i<=ed[p];i++){
              if(a[i]==d) ans++;
          }
        }
        
        if(dis[q]==vis[q]){
        if(dis[q]==d)
          ans+=r-st[q]+1;
        else ans+=0;
      }
      else {
          for(int i=st[q];i<=r;i++){
              if(a[i]==d) ans++;
          }
        }
        
         for(int i=p+1;i<q;i++) {
           if(dis[i]==vis[i]){
             if(dis[i]==d) ans+=(ed[i]-st[i]+1);             
           }else {
             for(int j=st[i];j<=ed[i];j++){
               if(a[j]==d) ans++;
             }
           }
         }
    }
    return ans;
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%lld",&a[i]);        
    }
    build();
    m=n;
    while(m--){
        //char ch;
        //fr(ch);
        ll l,r,d;
        scanf("%lld%lld%lld",&l,&r,&d);
        printf("%lld\n",query(l,r,d));
        change(l,r,d);
    }
    return 0;
}

数列分块入门 9

区间最小众数

#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn = 1e5 + 7;

ll n, m, block;
ll a[maxn], b[maxn], pos[maxn], cnt[maxn], ans[maxn];
vector<ll> v[maxn];
map<ll, ll>mp;
ll dp[510][510];

void change(ll x)
{
    memset(cnt, 0, sizeof cnt);
    ll maxx = 0, num = 0;
    for(int i = (x - 1) * block + 1; i <= n; i++)
    {
        ll c = ++cnt[a[i]];
        if(c > num || c == num && b[a[i]] < b[maxx])
        {
            maxx = a[i];
            num = c;
        }
        dp[x][pos[i]] = maxx;
    }
}
ll alonf(ll l, ll r, ll x)
{
    return upper_bound(v[x].begin(), v[x].end(), r) - lower_bound(v[x].begin(), v[x].end(), l);
}
ll query(ll l, ll r)
{
    ll ans=0,num=0;
    ll p = pos[l], q = pos[r];
    ans = dp[p + 1][q - 1];
    num = alonf(l, r, ans);
    for(int i = l; i <= min(r, p * block); i++)
    {
        ll c = alonf(l, r, a[i]);
        if(c > num || c == num && b[a[i]] < b[ans])
        {
            ans = a[i];
            num = c;
        }
    }
    if(p != q)
    {
        for(int i = (q - 1) * block + 1; i <= r; i++)
        {
            ll c = alonf(l, r, a[i]);
            if(c > num || c == num && b[a[i]] < b[ans])
            {
                ans = a[i];
                num = c;
            }
        }
    }
    return ans;
}
int main()
{
    scanf("%lld", &n);
    m = n;
    block = sqrt(n);
    ll idx = 0;
    for(ll i = 1; i <= n; i++)
    {
        scanf("%lld", &a[i]);
        pos[i] = (i - 1) / block + 1;
        if(mp[a[i]] == 0)
        {
            mp[a[i]] = ++idx;
            b[idx] = a[i];
        }
        a[i] = mp[a[i]];
        v[a[i]].push_back(i);
    }

    for(ll i = 1; i <= pos[n]; i++) change(i);
    
    
    while(m--)
    {
        ll l, r;
        scanf("%lld%lld", &l, &r);
        if(l > r) swap(l, r);
        printf("%lld\n", b[query(l, r)]);
    }
    return 0;
}









  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值