分块入门1~9题解

分块入门1:LOJ6277.
题目大意:给定一个长度为 n n n的序列以及 n n n个操作,操作设计区间加法,单点查值.
1 ≤ n ≤ 5 ∗ 1 0 4 1\leq n\leq 5*10^4 1n5104.

分块,每个块维护一个 t a g tag tag表示这个块被整体加了多少.

代码如下:

#include
  using namespace std;

#define Abigail inline void
typedef long long LL;

const int N=50000;

int n,a[N+9];
struct block{
  int tag,l,r;
}bl[N+9];
int bel[N+9],siz,cb;

void Build(int n){
  siz=300;
  while (bl[cb].r<n){
    ++cb;
    bl[cb].l=bl[cb-1].r+1;bl[cb].r=min(bl[cb].l+siz-1,n);
    for (int i=bl[cb].l;i<=bl[cb].r;++i) bel[i]=cb;
  }
}

void Change_add(int l,int r,int v){
  int lb=bel[l],rb=bel[r];
  if (lb+1>=rb){
  	for (int i=l;i<=r;++i) a[i]+=v;
    return;
  }
  for (int i=l;i<=bl[lb].r;++i) a[i]+=v;
  for (int i=r;i>=bl[rb].l;--i) a[i]+=v;
  for (int i=lb+1;i<rb;++i) bl[i].tag+=v;
}

int Query(int x){return a[x]+bl[bel[x]].tag;}

Abigail into(){
  scanf("%d",&n);
  for (int i=1;i<=n;++i)
    scanf("%d",&a[i]);
}

Abigail work(){
  Build(n);
}

Abigail getans(){
  int opt,l,r,c;
  for (int i=1;i<=n;++i){
  	scanf("%d%d%d%d",&opt,&l,&r,&c);
  	if (opt) printf("%d\n",Query(r));
	else Change_add(l,r,c);
  } 
}

int main(){
  into();
  work();
  getans();
  return 0;
}

分块入门2:LOJ6278.
题目大意:给定一个长为 n n n的序列与 n n n个操作,要求支持区间加法和区间查询小于某个给定值 x x x的元素个数.
1 ≤ n ≤ 5 ∗ 1 0 4 1\leq n\leq 5*10^4 1n5104.

仍然考虑分块.

查询区间内小于 x x x的元素个数的时候,不完整的块肯定大力搞,完整的块怎么处理?

考虑事先给完整块内的元素排序,然后二分找到第一个小于 x x x的位置,这个可以通过预处理搞.

那么区间加法怎么搞?完整的块可以直接打标记,二分的时候减掉这个值再二分,不完整的块可以直接大力加,事后这两个块也应该重新排序.

取块大小为 n \sqrt{n} n ,时间复杂度复杂度为 O ( n n log ⁡ n ) O(n\sqrt{n}\log n) O(nn logn).

代码如下:

#include
  using namespace std;

#define Abigail inline void
typedef long long LL;

const int N=50000,G=300;

int n,a[N+9];
struct block{
  int tag,l,r,v[G+9];
}bl[G+9];
int bel[N+9],siz,cb;

int Siz(int x){return bl[x].r-bl[x].l+1;}
int Val(int x){return a[x]+bl[bel[x]].tag;}

void Get_block(int k){
  for (int i=bl[k].l;i<=bl[k].r;++i) bl[k].v[i-bl[k].l+1]=a[i];
  sort(bl[k].v+1,bl[k].v+1+Siz(k));
}

void Build(int n){
  siz=300;
  while (bl[cb].r<n){
  	++cb;
    bl[cb].l=bl[cb-1].r+1,bl[cb].r=min(bl[cb].l+siz-1,n);
    for (int i=bl[cb].l;i<=bl[cb].r;++i) bel[i]=cb;
    Get_block(cb);
  }
}

void Change_add(int l,int r,int v){
  int lb=bel[l],rb=bel[r];
  if (lb+1>=rb){
  	for (int i=l;i<=r;++i) a[i]+=v;
  	Get_block(lb);Get_block(rb); 
	return;
  }
  for (int i=l;i<=bl[lb].r;++i) a[i]+=v;
  for (int i=r;i>=bl[rb].l;--i) a[i]+=v;
  Get_block(lb);Get_block(rb);
  for (int i=lb+1;i<rb;++i) bl[i].tag+=v;
}

int Lower(int *a,int n,int k){
  int l=0,r=n,mid=l+r+1>>1;
  for (;l<r;mid=l+r+1>>1)
    a[mid]<k?l=mid:r=mid-1;
  return l;
}

int Query_lower(int l,int r,int x){
  int lb=bel[l],rb=bel[r],res=0;
  if (lb+1>=rb){
  	for (int i=l;i<=r;++i)
  	  if (Val(i)<x) ++res;
  	return res;
  }
  for (int i=l;i<=bl[lb].r;++i)
    if (Val(i)<x) ++res;
  for (int i=r;i>=bl[rb].l;--i)
    if (Val(i)<x) ++res;
  for (int i=lb+1;i<rb;++i)
    res+=Lower(bl[i].v,Siz(i),x-bl[i].tag);
  return res;
}

Abigail into(){
  scanf("%d",&n);
  for (int i=1;i<=n;++i)
    scanf("%d",&a[i]);
}

Abigail work(){
  Build(n);
}

Abigail getans(){
  int opt,l,r,c;
  for (int i=1;i<=n;++i){
  	scanf("%d%d%d%d",&opt,&l,&r,&c);
  	if (opt) printf("%d\n",Query_lower(l,r,c*c));
	else Change_add(l,r,c);
  }
}

int main(){
  into();
  work();
  getans();
  return 0;
}

分块入门3:LOJ6279.
题目大意:给定一个长为 n n n的序列与 n n n个操作,要求支持区间加法和区间查询小于某个给定值 x x x的最大元素.
1 ≤ n ≤ 1 0 5 1\leq n\leq 10^5 1n105.

跟分块2一样,直接排序+二分就行了.

代码如下:

#include
  using namespace std;

#define Abigail inline void
typedef long long LL;

const int N=100000,G=400;

int n,a[N+9];
struct block{
  int tag,l,r,v[G+9];
}bl[G+9];
int bel[N+9],siz,cb;

int Siz(int x){return bl[x].r-bl[x].l+1;}
int Val(int x){return a[x]+bl[bel[x]].tag;}

void Get_block(int k){
  for (int i=bl[k].l;i<=bl[k].r;++i) bl[k].v[i-bl[k].l+1]=a[i];
  sort(bl[k].v+1,bl[k].v+1+Siz(k));
}

void Build(int n){
  siz=400;
  while (bl[cb].r<n){
  	++cb;
  	bl[cb].l=bl[cb-1].r+1;bl[cb].r=min(bl[cb].l+siz-1,n);
  	for (int i=bl[cb].l;i<=bl[cb].r;++i) bel[i]=cb;
  	Get_block(cb);
  }
}

void Change_add(int l,int r,int v){
  int lb=bel[l],rb=bel[r];
  if (lb+1>=rb){
  	for (int i=l;i<=r;++i) a[i]+=v;
  	Get_block(lb);Get_block(rb);
  	return;
  }
  for (int i=l;i<=bl[lb].r;++i) a[i]+=v;
  for (int i=r;i>=bl[rb].l;--i) a[i]+=v;
  Get_block(lb);Get_block(rb);
  for (int i=lb+1;i<rb;++i) bl[i].tag+=v;
}

int Lower(int *a,int n,int k){
  int l=0,r=n,mid=l+r+1>>1;
  for (;l<r;mid=l+r+1>>1)
    a[mid]>=k?r=mid-1:l=mid;
  return l==0?-1:a[l];
}

int Query_lower(int l,int r,int x){
  int res=-1,lb=bel[l],rb=bel[r],tmp;
  if (lb+1>=rb){
  	for (int i=l;i<=r;++i)
  	  if (Val(i)<x) res=max(res,Val(i));
  	return res;
  }
  for (int i=l;i<=bl[lb].r;++i)
    if (Val(i)<x) res=max(res,Val(i));
  for (int i=r;i>=bl[rb].l;--i)
    if (Val(i)<x) res=max(res,Val(i));
  for (int i=lb+1;i<rb;++i){
    tmp=Lower(bl[i].v,Siz(i),x-bl[i].tag);
    if (tmp>-1) res=max(res,tmp+bl[i].tag);
  }
  return res;
}

Abigail into(){
  scanf("%d",&n);
  for (int i=1;i<=n;++i)
    scanf("%d",&a[i]);
}

Abigail work(){
  Build(n);
}

Abigail getans(){
  int opt,l,r,c;
  for (int i=1;i<=n;++i){
  	scanf("%d%d%d%d",&opt,&l,&r,&c);
  	if (opt) printf("%d\n",Query_lower(l,r,c));
	else Change_add(l,r,c);
  }
}

int main(){
  into();
  work();
  getans();
  return 0;
}

分块入门4:LOJ6280.
题目大意:给定一个长度为 n n n的序列和 n n n个操作,支持区间加,区间查询和.
1 ≤ n ≤ 5 ∗ 1 0 4 1\leq n\leq 5*10^4 1n5104.

在分块1的基础上维护每个块的和即可.

代码如下:

#include
  using namespace std;

#define Abigail inline void
typedef long long LL;

const int N=50000,G=300;

int n;
LL a[N+9];
struct block{
  int l,r;
  LL tag,sum;
}bl[N+9];
int bel[N+9],siz,cb;

int Siz(int x){return bl[x].r-bl[x].l+1;}
LL Val(int x){return a[x]+bl[bel[x]].tag;}
void Get_block(int k){bl[k].sum=0;for (int i=bl[k].l;i<=bl[k].r;++i) bl[k].sum+=Val(i);}
void Block_add(int k,LL v){bl[k].tag+=v;bl[k].sum+=v*Siz(k);}

void Build(int n){
  siz=300;
  while (bl[cb].r<n){
  	++cb;
  	bl[cb].l=bl[cb-1].r+1;bl[cb].r=min(bl[cb].l+siz-1,n);
  	for (int i=bl[cb].l;i<=bl[cb].r;++i) bel[i]=cb;
  	Get_block(cb);
  }
}

void Change_add(int l,int r,LL v){
  int lb=bel[l],rb=bel[r];
  if (lb+1>=rb){
    for (int i=l;i<=r;++i) a[i]+=v;
    Get_block(lb);Get_block(rb);
    return;
  }
  for (int i=l;i<=bl[lb].r;++i) a[i]+=v;
  for (int i=r;i>=bl[rb].l;--i) a[i]+=v;
  Get_block(lb);Get_block(rb);
  for (int i=lb+1;i<rb;++i) Block_add(i,v);
}

LL Query(int l,int r){
  int lb=bel[l],rb=bel[r];
  LL res=0;
  if (lb+1>=rb){
  	for (int i=l;i<=r;++i) res+=Val(i);
  	return res;
  }
  for (int i=l;i<=bl[lb].r;++i) res+=Val(i);
  for (int i=r;i>=bl[rb].l;--i) res+=Val(i);
  for (int i=lb+1;i<rb;++i) res+=bl[i].sum;
  return res;
}

Abigail into(){
  scanf("%d",&n);
  for (int i=1;i<=n;++i)
    scanf("%lld",&a[i]);
}

Abigail work(){
  Build(n);
}

Abigail getans(){
  int opt,l,r;
  LL c;
  for (int i=1;i<=n;++i){
  	scanf("%d%d%d%lld",&opt,&l,&r,&c);
  	if (opt) printf("%lld\n",Query(l,r)%(c+1));
	else Change_add(l,r,c);
  }
}

int main(){
  into();
  work();
  getans();
  return 0;
}

分块入门5:LOJ6281.
题目大意:给定一个长度为 n n n的序列和 n n n个操作,支持区间开方(下取整),区间查询和.
1 ≤ n ≤ 5 ∗ 1 0 4 1\leq n\leq 5*10^4 1n5104.

一个区间的和开方并不等于与区间内每一个值的开方之和,即 ⌊ ∑ a i ⌋ = ̸ ∑ ⌊ a i ⌋ \left\lfloor\sum a_i\right\rfloor =\not{} \sum \left\lfloor a_i\right\rfloor ai≠ai,不能直接维护了,怎么办?

容易发现 0 0 0 1 1 1开方是不变的,而且一个数开方几次就变成 0 0 0 1 1 1了.

什么意思,也就是说我们直接大力维护一个区间是否全是 0 0 0 1 1 1,如果不是大力给每一个数开方,否则直接什么都不干就可以了.

时间复杂度的话,容易发现数 x x x开方 O ( log ⁡ log ⁡ x ) O(\log \log x) O(loglogx)次就会变成 0 0 0 1 1 1(每次位数减半),所以复杂度是 O ( n n log ⁡ log ⁡ n ) O(n\sqrt{n}\log\log n) O(nn loglogn).

代码如下:

#include
  using namespace std;

#define Abigail inline void
typedef long long LL;

const int N=50000,G=300;

int n,a[N+9];
struct block{
  int l,r,tag,sum;
}bl[N+9];
int bel[N+9],cb,siz;

int Siz(int x){return bl[x].r-bl[x].l+1;}
void Get_block(int k){
  bl[k].sum=bl[k].tag=0;
  for (int i=bl[k].l;i<=bl[k].r;++i){
    bl[k].sum+=a[i];
    if (a[i]>1) bl[k].tag=1;
  }
}

void Build(int n){
  siz=300;
  while (bl[cb].r<n){
  	++cb;
  	bl[cb].l=bl[cb-1].r+1;bl[cb].r=min(bl[cb].l+siz-1,n);
  	for (int i=bl[cb].l;i<=bl[cb].r;++i) bel[i]=cb;
  	Get_block(cb);
  }
}

void Block_sqrt(int k){
  if (!bl[k].tag) return;
  bl[k].tag=bl[k].sum=0;
  for (int i=bl[k].l;i<=bl[k].r;++i){
  	a[i]=(int)sqrt(a[i]);
  	bl[k].sum+=a[i];
  	if (a[i]>1) bl[k].tag=1;
  }
}

void Change_sqrt(int l,int r){
  int lb=bel[l],rb=bel[r];
  if (lb+1>=rb){
  	for (int i=l;i<=r;++i) a[i]=(int)sqrt(a[i]);
  	Get_block(lb);Get_block(rb);
  	return;
  }
  for (int i=l;i<=bl[lb].r;++i) a[i]=(int)sqrt(a[i]);
  for (int i=r;i>=bl[rb].l;--i) a[i]=(int)sqrt(a[i]);
  Get_block(lb);Get_block(rb);
  for (int i=lb+1;i<rb;++i) Block_sqrt(i);
}

int Query(int l,int r){
  int lb=bel[l],rb=bel[r],res=0;
  if (lb+1>=rb){
  	for (int i=l;i<=r;++i) res+=a[i];
  	return res;
  }
  for (int i=l;i<=bl[lb].r;++i) res+=a[i];
  for (int i=r;i>=bl[rb].l;--i) res+=a[i];
  for (int i=lb+1;i<rb;++i) res+=bl[i].sum;
  return res;
}

Abigail into(){
  scanf("%d",&n);
  for (int i=1;i<=n;++i)
    scanf("%d",&a[i]);
}

Abigail work(){
  Build(n);
}

Abigail getans(){
  int opt,l,r,c;
  for (int i=1;i<=n;++i){
  	scanf("%d%d%d%d",&opt,&l,&r,&c);
  	if (opt) printf("%d\n",Query(l,r));
	else Change_sqrt(l,r);
  }
}

int main(){
  into();
  work();
  getans();
  return 0;
}

分块入门6:LOJ6282.
题目大意:给定一个长度为 n n n的序列和 n n n个操作,支持单点插入,单点查询.
1 ≤ n ≤ 1 0 5 1\leq n\leq 10^5 1n105,数据随机.

平衡树板子…

考虑分块,每隔 n \sqrt{n} n 个元素分成一个块,每个块内部写个数组或链表,再把所有块塞到一个数组或链表里,这样子在数据随机的情况下复杂度 O ( n n ) O(n\sqrt{n}) O(nn ).

若数据不随机?有两种解决方案:
1.每隔 O ( n ) O(\sqrt{n}) O(n )个操作把块重构一次,这样子可以保证每一时刻块的大小不会超过 O ( n ) O(\sqrt{n}) O(n ).重构每次 O ( n ) O(n) O(n),总共 O ( n ) O(\sqrt{n}) O(n )次重构,所有时间复杂度仍然是 O ( n n ) O(n\sqrt{n}) O(nn )的.
2.当块的大小超过 2 n 2\sqrt{n} 2n 时把块从中间分裂,这样子复杂度也是 O ( n n ) O(n\sqrt{n}) O(nn )的.

方案1代码如下:

#include
  using namespace std;

#define Abigail inline void
typedef long long LL;

const int N=100000,G=400;

int n,a[N*2+9];
struct block{
  int siz,v[G*2+9],last,next;
}bl[G*2+9];
int cb,siz,tmp[N*2+9];

void Build(int n){siz=400;}
int New_block(int x){bl[++cb].v[1]=x;bl[cb].siz=1;return cb;}

void Block_insert(int x,int l,int r){
  bl[x].last=l;bl[x].next=r;
  bl[l].next=x;bl[r].last=x;
}

void Rebuild(){
  int t,n=0,r,hr;
  for (t=bl[0].next;t;t=bl[t].next)
    for (int i=1;i<=bl[t].siz;++i)
      tmp[++n]=bl[t].v[i];
  cb=0;
  for (r=hr=0;r<n;){
  	++cb;
  	hr=r;r=min(r+siz-1,n);
  	for (int i=hr+1;i<=r;++i) bl[cb].v[i-hr]=tmp[i];
  	bl[cb].siz=r-hr;
  	Block_insert(cb,cb-1,0);
  }
}

void Insert(int p,int x){
  int t=bl[0].next,ht=0;
  for (;t&&p>bl[t].siz+1;ht=t,t=bl[t].next) p-=bl[t].siz;
  if (!t) Block_insert(t=New_block(x),ht,0);
  else{
  	for (int i=bl[t].siz;i>=p;--i) bl[t].v[i+1]=bl[t].v[i];
  	bl[t].v[p]=x;++bl[t].siz;
  }
}

int Query(int p){
  int t=bl[0].next;
  while (t&&p>bl[t].siz) p-=bl[t].siz,t=bl[t].next;
  return t?bl[t].v[p]:-1;
}

Abigail into(){
  scanf("%d",&n);
  for (int i=1;i<=n;++i)
    scanf("%d",&a[i]);
}

Abigail work(){
  Build(n);
  for (int i=1;i<=n;++i) {
    Insert(i,a[i]);
    if (i%siz==0) Rebuild();
  }
}

Abigail getans(){
  int opt,l,r,c;
  for (int i=1;i<=n;++i){
  	scanf("%d%d%d%d",&opt,&l,&r,&c);
  	if (opt) printf("%d\n",Query(r));
	else Insert(l,r);
	if (i%siz==0) Rebuild();
  }
}

int main(){
  into();
  work();
  getans();
  return 0;
}

方案2代码如下:

#include
  using namespace std;

#define Abigail inline void
typedef long long LL;

const int N=100000,G=400;

int n,a[N*2+9];
struct block{
  int siz,v[G*2+9],last,next;
}bl[G*2+9];
int cb,siz,tmp[N*2+9];

void Build(int n){siz=400;}
int New_block(int x){bl[++cb].v[1]=x;bl[cb].siz=1;return cb;}
int New_block(){bl[++cb].siz=0;return cb;}

void Link(int k,int l,int r){
  bl[k].last=l;bl[k].next=r;
  bl[l].next=k;bl[r].last=k;
}

void Split(int k,int mid){
  int t=New_block();
  for (int i=mid+1;i<=bl[k].siz;++i)
    bl[t].v[++bl[t].siz]=bl[k].v[i];
  bl[k].siz=mid;
  Link(t,k,bl[k].next);
}

void Insert(int p,int x){
  int t=bl[0].next,ht=0;
  for (;t&&p>bl[t].siz+1;ht=t,t=bl[t].next) p-=bl[t].siz;
  if (!t) Link(t=New_block(x),ht,0);
  else{
  	for (int i=bl[t].siz;i>=p;--i) bl[t].v[i+1]=bl[t].v[i];
  	bl[t].v[p]=x;++bl[t].siz;
  }
  if (bl[t].siz>siz<<1) Split(t,bl[t].siz+1>>1);
}

int Query(int p){
  int t=bl[0].next;
  while (t&&p>bl[t].siz) p-=bl[t].siz,t=bl[t].next;
  return t?bl[t].v[p]:-1;
}

Abigail into(){
  scanf("%d",&n);
  for (int i=1;i<=n;++i)
    scanf("%d",&a[i]);
}

Abigail work(){
  Build(n);
  for (int i=1;i<=n;++i)
    Insert(i,a[i]);
}

Abigail getans(){
  int opt,l,r,c;
  for (int i=1;i<=n;++i){
  	scanf("%d%d%d%d",&opt,&l,&r,&c);
  	if (opt) printf("%d\n",Query(r));
	else Insert(l,r);
  }
}

int main(){
  into();
  work();
  getans();
  return 0;
}

分块入门7:LOJ6283.
题目大意:给定一个长度为 n n n的序列以及 n n n个操作,支持区间加法、区间乘法和单点查值.
1 ≤ n ≤ 1 0 5 1\leq n\leq 10^5 1n105.

与分块1差不多,只是需要维护两个tag,为了方便可以在要查值的时候直接把当前块的标记下传.

代码如下:

#include
  using namespace std;

#define Abigail inline void
typedef long long LL;

const int N=100000,mod=10007;

int n,a[N+9];
struct block{
  int l,r,add,mul;
}bl[N+9];
int cb,siz,bel[N+9];

void Pushdown(int k){
  for (int i=bl[k].l;i<=bl[k].r;++i)
  	a[i]=(a[i]*bl[k].mul+bl[k].add)%mod;
  bl[k].mul=1;bl[k].add=0;
}

void Build(int n){
  siz=400;
  while (bl[cb].r<n){
  	++cb;
  	bl[cb].l=bl[cb-1].r+1;bl[cb].r=min(bl[cb].l+siz-1,n);
  	for (int i=bl[cb].l;i<=bl[cb].r;++i) bel[i]=cb;
  	bl[cb].mul=1;
  }
}

void Update_add(int k,int v){bl[k].add=(bl[k].add+v)%mod;}
void Update_mul(int k,int v){bl[k].add=bl[k].add*v%mod;bl[k].mul=bl[k].mul*v%mod;}

void Change_add(int l,int r,int v){
  int lb=bel[l],rb=bel[r];
  Pushdown(lb);Pushdown(rb);
  if (lb+1>=rb){
  	for (int i=l;i<=r;++i) a[i]=(a[i]+v)%mod;
  	return;
  }
  for (int i=l;i<=bl[lb].r;++i) a[i]=(a[i]+v)%mod;
  for (int i=r;i>=bl[rb].l;--i) a[i]=(a[i]+v)%mod;
  for (int i=lb+1;i<rb;++i) Update_add(i,v);
}

void Change_mul(int l,int r,int v){
  int lb=bel[l],rb=bel[r];
  Pushdown(lb);Pushdown(rb);
  if (lb+1>=rb){
  	for (int i=l;i<=r;++i) a[i]=a[i]*v%mod;
  	return;
  }
  for (int i=l;i<=bl[lb].r;++i) a[i]=a[i]*v%mod;
  for (int i=r;i>=bl[rb].l;--i) a[i]=a[i]*v%mod;
  for (int i=lb+1;i<rb;++i) Update_mul(i,v);
}

int Query(int p){Pushdown(bel[p]);return a[p];}

Abigail into(){
  scanf("%d",&n);
  for (int i=1;i<=n;++i)
    scanf("%d",&a[i]);
}

Abigail work(){
  Build(n);
}

Abigail getans(){
  int opt,l,r,c;
  for (int i=1;i<=n;++i){
  	scanf("%d%d%d%d",&opt,&l,&r,&c);
  	switch (opt){
	  case 0:
	  	Change_add(l,r,c);
	  	break;
	  case 1:
	  	Change_mul(l,r,c);
	  	break;
	  case 2:
	  	printf("%d\n",Query(r));
	}
  }
}

int main(){
  into();
  work();
  getans();
  return 0;
}

分块入门8:LOJ6284.
题目大意:给定一个长度为 n n n的序列以及 n n n个操作,支持查询区间内等于 c c c的数的个数且把这个区间覆盖为 c c c.
1 ≤ n ≤ 1 0 5 1\leq n\leq 10^5 1n105.

仍然是分块,对于每个整块记录一下这个块是否全部是同一个值,是则直接判,不是就大力跑一遍,然后覆盖即可.

现在我们考虑分析这个算法的复杂度.首先我们看成这个序列初始全部为 0 0 0,然后有 n n n次修改成了输入的序列.然后考虑每一次覆盖会导致最多两个块出现不等于一个值的情况,每次查询遇到不同就会直接把它变成相同,所以出现不同的情况数是 O ( n ) O(n) O(n)的.因为每次处理需要 O ( n ) O(\sqrt{n}) O(n )的时间,所以总时间复杂度为 O ( n n ) O(n\sqrt{n}) O(nn )的.

代码如下:

#include
  using namespace std;

#define Abigail inline void
typedef long long LL;

const int N=100000;

int n,a[N+9];
struct block{
  int l,r,tag,col;
}bl[N+9];
int cb,siz,bel[N+9];

int Siz(int k){return bl[k].r-bl[k].l+1;}

void Pushup(int k){
  bl[k].tag=1;bl[k].col=a[bl[k].l];
  for (int i=bl[k].l+1;i<=bl[k].r;++i)
    if (a[i]^a[i-1]) bl[k].tag=0;
}

void Pushdown(int k){
  if (!bl[k].tag) return;
  for (int i=bl[k].l;i<=bl[k].r;++i) a[i]=bl[k].col;
}

void Build(int n){
  siz=400;
  while (bl[cb].r<n){
  	++cb;
  	bl[cb].l=bl[cb-1].r+1;bl[cb].r=min(bl[cb].l+siz-1,n);
  	for (int i=bl[cb].l;i<=bl[cb].r;++i) bel[i]=cb;
  	Pushup(cb);
  }
}

void Update(int k,int v){bl[k].tag=1;bl[k].col=v;}

void Change(int l,int r,int v){
  int lb=bel[l],rb=bel[r];
  Pushdown(lb);Pushdown(rb);
  if (lb+1>=rb){
  	for (int i=l;i<=r;++i) a[i]=v;
  	Pushup(lb);Pushup(rb);
  	return;
  }
  for (int i=l;i<=bl[lb].r;++i) a[i]=v;
  for (int i=r;i>=bl[rb].l;--i) a[i]=v;
  Pushup(lb);Pushup(rb);
  for (int i=lb+1;i<rb;++i) Update(i,v);
}

int Query(int l,int r,int v){
  int lb=bel[l],rb=bel[r],res=0;
  Pushdown(lb);Pushdown(rb);
  if (lb+1>=rb){
  	for (int i=l;i<=r;++i) res+=a[i]==v;
  	return res;
  }
  for (int i=l;i<=bl[lb].r;++i) res+=a[i]==v;
  for (int i=r;i>=bl[rb].l;--i) res+=a[i]==v;
  for (int i=lb+1;i<rb;++i)
    if (bl[i].tag) res+=(bl[i].col==v)*Siz(i);
    else {
      for (int j=bl[i].l;j<=bl[i].r;++j)
        res+=a[j]==v;
	}
  return res;
}

Abigail into(){
  scanf("%d",&n);
  for (int i=1;i<=n;++i)
    scanf("%d",&a[i]);
}

Abigail work(){
  Build(n);
}

Abigail getans(){
  int l,r,c;
  for (int i=1;i<=n;++i){
  	scanf("%d%d%d",&l,&r,&c);
  	printf("%d\n",Query(l,r,c));
  	Change(l,r,c);
  }
}

int main(){
  into();
  work();
  getans();
  return 0;
}

分块入门9:LOJ6285.
题目大意:给定一个长度为 n n n的序列以及 n n n个操作,支持查询区间内的最小众数.
1 ≤ n ≤ 1 0 5 1\leq n\leq 10^5 1n105.

莫队板子…

众数并没有区间加法性质,所以不能直接用线段树树状数组什么的了…

考虑分块,容易发现一个区间内的众数只有可能是两种情况,即完整块的众数或不完整块中的数.

设块数为 k k k,我们把块写成一个序列的形式,维护这个序列上每个区间内每个数的出现次数,这可以 O ( n k 2 ) O(nk^2) O(nk2)预处理出来,空间复杂度 O ( n k 2 ) O(nk^2) O(nk2).

然后对于每个区间,我们可以直接把两边非完整块中出现的数对于出现次数的贡献加到预处理出的完整块的信息中,顺便处理出众数,等下再去掉即可,时间复杂度一次 O ( n k ) O(\frac{n}{k}) O(kn).

然后我们计算最优的 k k k,容易发现 k = n 1 3 k=n^{\frac{1}{3}} k=n31时最优,时空复杂度均为 O ( n 5 3 ) O(n^{\frac{5}{3}}) O(n35).实测可以拿到 88 88 88分的高分,经过一波常数优化最终卡到了 92 92 92分的高分优化不下去了,这里就不贴代码了…

如何得到一个复杂度更优秀的做法呢?我们仍然考虑预处理出所有块区间的众数,同时需要对于每个数值 i i i保存一个vector p [ i ] [ j ] p[i][j] p[i][j],里面按顺序保存数值 i i i的出现位置.

然后对于每一次询问,对于每个不完整块中的元素,我们暴力在vector中二分查找它在区间中出现的第一个位置和最后一个位置来得到它的出现次数,更新答案即可.

设块的数量为 k k k,则预处理复杂度为 O ( n k ) O(nk) O(nk),询问复杂度为 O ( n n k log ⁡ n ) O(n\frac{n}{k}\log n) O(nknlogn),发现取 k = n log ⁡ n k=\sqrt{n\log n} k=nlogn 最优,时空复杂度为 O ( n n log ⁡ n ) O(n\sqrt{n\log n}) O(nnlogn ).

其实我们还可以把二分去掉.考虑用分块,计算出第 1 1 1个块到第 i i i个块中权值 x x x的出现次数 c [ i ] [ x ] c[i][x] c[i][x],这个可以 O ( n n ) O(n\sqrt{n}) O(nn )预处理.然后对于区间 [ l , r ] [l,r] [l,r],假设它包含了完整的块区间 [ x , y ] [x,y] [x,y],那么计算完整块内一个权值 k k k的出现数量可以通过 c [ y ] [ k ] − c [ x − 1 ] [ k ] c[y][k]-c[x-1][k] c[y][k]c[x1][k]来得到,然后右边不完整的块的贡献暴力算入 c [ y ] [ k ] c[y][k] c[y][k]里,得到答案之后再把贡献去掉即可.

这个做法的时空复杂度均为 O ( n n ) O(n\sqrt{n}) O(nn ).

O ( n n log ⁡ n ) O(n\sqrt{n\log n}) O(nnlogn )代码如下:

#include
  using namespace std;

#define Abigail inline void
typedef long long LL;

const int N=100000,G=1400;

int Lower(int *a,int n,int x){
  int l=1,r=n,mid=l+r>>1;
  for (;l<r;mid=l+r>>1)
    x>a[mid]?l=mid+1:r=mid;
  return l;
}

int n,a[N+9],ord[N+9];
struct block{
  int l,r;
}bl[G+9];
int cb,siz,bel[N+9];
int ans[G+9][G+9],cnt[G+9][G+9],tmp[N+9];
vector<int>p[N+9];

void Modify(int a,int b,int &x,int &y){if (a>x||a==x&&b<y) x=a,y=b;}

void Build(int n){
  siz=75;
  while (bl[cb].r<n){
  	++cb;
  	bl[cb].l=bl[cb-1].r+1;bl[cb].r=min(bl[cb].l+siz-1,n);
  	for (int i=bl[cb].l;i<=bl[cb].r;++i) bel[i]=cb;
  }
  for (int i=1;i<=cb;++i){
    for (int j=i;j<=cb;++j){
      ans[i][j]=ans[i][j-1];cnt[i][j]=cnt[i][j-1];
	  for (int k=bl[j].l;k<=bl[j].r;++k) ++tmp[a[k]],Modify(tmp[a[k]],a[k],cnt[i][j],ans[i][j]);
	}
	for (int k=bl[i].l;k<=n;++k) --tmp[a[k]];
  }
}

int Count_lower(int x,int pf){
  int l=0,r=p[x].size()-1,mid=l+r>>1;
  for (;l<r;mid=l+r>>1)
    pf>p[x][mid]?l=mid+1:r=mid;
  return l;
}

int Count_upper(int x,int pf){
  int l=0,r=p[x].size()-1,mid=l+r+1>>1;
  for (;l<r;mid=l+r+1>>1)
    pf<p[x][mid]?r=mid-1:l=mid;
  return r;
}

int Count(int x,int l,int r){return Count_upper(x,r)-Count_lower(x,l)+1;}

int Query(int l,int r){
  int lb=bel[l],rb=bel[r],res=0,num=0;
  if (lb+1>=rb){
  	for (int i=l;i<=r;++i) Modify(Count(a[i],l,r),a[i],num,res);
  	return res;
  }
  res=ans[lb+1][rb-1];num=cnt[lb+1][rb-1];
  for (int i=l;i<=bl[lb].r;++i) Modify(Count(a[i],l,r),a[i],num,res);
  for (int i=r;i>=bl[rb].l;--i) Modify(Count(a[i],l,r),a[i],num,res);
  return res;
}

Abigail into(){
  scanf("%d",&n);
  for (int i=1;i<=n;++i){
  	scanf("%d",&a[i]);
  	ord[i]=a[i];
  }
}

Abigail work(){
  sort(ord+1,ord+1+n);
  for (int i=1;i<=n;++i){
    a[i]=Lower(ord,n,a[i]);
    p[a[i]].push_back(i);
  }
  Build(n);
}

Abigail outo(){
  int l,r;
  for (int i=1;i<=n;++i){
  	scanf("%d%d",&l,&r);
  	printf("%d\n",ord[Query(l,r)]);
  }
}

int main(){
  into();
  work();
  outo();
  return 0;
}

O ( n n ) O(n\sqrt{n}) O(nn )代码如下:

#include
  using namespace std;

#define Abigail inline void
typedef long long LL;

const int N=100000,G=400;

int Lower(int *a,int n,int x){
  int l=1,r=n,mid=l+r>>1;
  for (;l<r;mid=l+r>>1)
    x>a[mid]?l=mid+1:r=mid;
  return l;
}

int n,a[N+9],ord[N+9];
struct block{
  int l,r;
}bl[G+9];
int cb,siz,bel[N+9];
int ans[G+9][G+9],cnt[G+9][G+9],tmp[N+9],c[G+9][N+9];

void Modify(int a,int b,int &x,int &y){if (a>x||a==x&&b<y) x=a,y=b;}

void Build(int n){
  siz=400;
  while (bl[cb].r<n){
  	++cb;
  	bl[cb].l=bl[cb-1].r+1;bl[cb].r=min(bl[cb].l+siz-1,n);
  	for (int i=bl[cb].l;i<=bl[cb].r;++i) bel[i]=cb;
  }
  for (int i=1;i<=cb;++i){
    for (int j=i;j<=cb;++j){
      ans[i][j]=ans[i][j-1];cnt[i][j]=cnt[i][j-1];
	  for (int k=bl[j].l;k<=bl[j].r;++k) ++tmp[a[k]],Modify(tmp[a[k]],a[k],cnt[i][j],ans[i][j]);
	}
	for (int k=bl[i].l;k<=n;++k) --tmp[a[k]];
	for (int j=1;j<=bl[i].r;++j) ++c[i][a[j]];
  }
}

int Query(int l,int r){
  int lb=bel[l],rb=bel[r],res=0,num=0;
  if (lb+1>=rb){
    for (int i=l;i<=r;++i) ++tmp[a[i]],Modify(tmp[a[i]],a[i],num,res);
    for (int i=l;i<=r;++i) --tmp[a[i]];
    return res;
  }
  res=ans[lb+1][rb-1];num=cnt[lb+1][rb-1];
  for (int i=l;i<=bl[lb].r;++i) ++c[rb-1][a[i]],Modify(c[rb-1][a[i]]-c[lb][a[i]],a[i],num,res);
  for (int i=r;i>=bl[rb].l;--i) ++c[rb-1][a[i]],Modify(c[rb-1][a[i]]-c[lb][a[i]],a[i],num,res);
  for (int i=l;i<=bl[lb].r;++i) --c[rb-1][a[i]];
  for (int i=r;i>=bl[rb].l;--i) --c[rb-1][a[i]];
  return res;
}

Abigail into(){
  scanf("%d",&n);
  for (int i=1;i<=n;++i){
  	scanf("%d",&a[i]);
  	ord[i]=a[i];
  }
}

Abigail work(){
  sort(ord+1,ord+1+n);
  for (int i=1;i<=n;++i)
    a[i]=Lower(ord,n,a[i]);
  Build(n);
}

Abigail outo(){
  int l,r;
  for (int i=1;i<=n;++i){
  	scanf("%d%d",&l,&r);
  	printf("%d\n",ord[Query(l,r)]);
  }
}

int main(){
  into();
  work();
  outo();
  return 0;
}

你可能感兴趣的:(分块入门1~9题解)