2021-05-19 「分块」数列分块入门1 – 9

数列分块入门 1

1.题意:

区间加法,单点查值

2.题解:

死活不知道开始为什么超时

3.ac代码:
#include <bits/stdc++.h>
#include <math.h>
using namespace std;
typedef long long ll;
const int N = 50000 + 10;
int a[N];
int n, m;
int st[N], ed[N], sz[N], sum[N], bel[N], mark[N];
inline void init(int n) { //分块
    int  sq = sqrt(n);

    for (int i = 1; i <= sq; i++) {
        st[i] = sq * (i - 1) + 1;
        ed[i] = sq * i;
        sz[i] = ed[i] - st[i] + 1;
    }

    ed[sq] = n;
    sz[sq] = ed[sq] - st[sq] + 1;

    for (int i = 1; i <= sq; i++) {
        for (int j = st[i]; j <= ed[i]; j++) {
            bel[j] = i;
            //  sum[i]+=a[j];
        }

    }
}
inline void change(int l, int r, int d) { //区间修改,加上一个数
    if (bel[l] == bel[r]) {
        for (int i = l; i <= r; i++) {
            a[i] += d;
            //  sum[bel[i]]+=d;
        }
    } else {
        for (int i = l; i <= ed[bel[l]]; i++)
            a[i] += d;

        // sum[bel[l]]+=d*(ed[bel[l]]-l+1);
        for (int i = st[bel[r]]; i <= r; i++)
            a[i] += d;

        //sum[bel[r]]+=d*(r-st[bel[r]]+1);
        for (int i = bel[l] + 1; i < bel[r]; i++)
            mark[i] += d;
    }
}
//ll query(int l,int r){//区间查询
//    ll ans=0;
//    if(bel[l]==bel[r]){
//        for(int i=l;i<=r;i++) ans+=a[i];
//        ans+=mark[bel[l]]*(r-l+1);
//    }else{
//        for(int i=l;i<=ed[bel[l]];i++) ans+=a[i];
//        ans+=mark[bel[l]]*(ed[bel[l]]-l+1);
//        for(int i=st[bel[r]];i<=r;i++) ans+=a[i];
//        ans+=mark[bel[r]]*(r-st[bel[r]]+1);
//        for(int i=bel[l]+1;i<bel[r];i++) ans+=sum[i]+mark[i]*sz[i];
//
//    }
//}

int main() {
    scanf("%d", &n);

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

    int l, r, op;
    int c;
    init(n);

    for (int i = 1; i <= n; i++) {
        scanf("%d%d%d%d", &op, &l, &r, &c);

        if (op == 0) {
            change(l, r, c);
        } else {
            printf("%d\n", a[r] + mark[bel[r]]);
        }
    }
}

数列分块入门 2

1.题意:

区间加法,询问区间内小于某个值 x的元素个数。

2.题解:

!!!快哭了啊啊啊啊啊啊,我终于知道我错哪了,卡了我快俩小时md

3.ac代码:
#include <bits/stdc++.h>
#include <math.h>
using namespace std;
typedef long long ll;
const int N = 50000 + 10;
int a[N];
int n, m;
int st[N], ed[N], sz[N], sum[N], bel[N], mark[N];
vector<int> v[N];
inline void init(int n) { //分块
    int  sq = sqrt(n);

    for (int i = 1; i <= sq; i++) {
        st[i] = sq * (i - 1) + 1;
        ed[i] = sq * i;
        sz[i] = ed[i] - st[i] + 1;      
    }

    ed[sq] = n;
    sz[sq] = ed[sq] - st[sq] + 1;

    for (int i = 1; i <= sq; i++) {
        for (int j = st[i]; j <= ed[i]; j++) {
            bel[j] = i;
            v[i].push_back(a[j]);
             sum[i]+=a[j];
        }
        sort(v[i].begin(),v[i].end());
    }
    
}
void reset(int i){
	
	for(int j=0;j<=sz[i];j++){
		v[i][j]=a[st[i]+j];
	}
	sort(v[i].begin(),v[i].end());
}
inline void change(int l, int r, int d) { //区间修改,加上一个数
    if (bel[l] == bel[r]) {
        for (int i = l; i <= r; i++) {
            a[i] += d;
        }
        reset(bel[l]);
       
    } else {
        for (int i = l; i <= ed[bel[l]]; i++)
            a[i] += d;
        reset(bel[l]);
        
        for (int i = st[bel[r]]; i <= r; i++)
            a[i] += d;
        reset(bel[r]);
        
        for (int i = bel[l] + 1; i < bel[r]; i++)
            mark[i] += d;
    }
}
int query(int l,int r,int c){//区间查询,小于c的个数
    int ans=0;
    if(bel[l]==bel[r]){
        for(int i=l;i<=r;i++){
            if(a[i]+mark[bel[i]]<c*c) ans++;
        }
    }else{
        for(int i=l;i<=ed[bel[l]];i++){
            if(a[i]+mark[bel[l]]<c*c) ans++;
        } 
       
        for(int i=st[bel[r]];i<=r;i++){
            if(a[i]+mark[bel[r]]<c*c) ans++;
        } 
         
        for(int i=bel[l]+1;i<bel[r];i++){
            ans+=lower_bound(v[i].begin(), v[i].end(),c*c-mark[i])-v[i].begin();//!!!mark【i】!!不是mark【bel【i】】
        }

    }
    return ans; 
}

int main() {
    cin>>n;

    for (int i = 1; i <= n; i++)
        cin>>a[i];

    int l, r, op;
    ll c;
    init(n);

    for (int i = 1; i <= n; i++) {
        cin>>op>>l>>r>>c;

        if (op == 0) {
            change(l, r, c);
        } else {
           cout<<query(l,r,c)<<endl;
        }
    }
}

数列分块入门 3

1.题意:

区间加法+询区间内比x小的最大元素

2.题解:

在 [first, last) 区域内查找

  • lower_bound(,x) 第一个>=x

   int a[5] = { 1,2,3,4,5 };
    //从 a 数组中找到第一个不小于 3 的元素
    int *p = lower_bound(a, a + 5, 3);

*p=3

  • upper bound(,x) 第一个>x
int a[5] = { 1,2,3,4,5 };
    //从 a 数组中找到第一个大于 3 的元素
    int *p = upper_bound(a, a + 5, 3);

*p = 4

3.ac代码:
#include <bits/stdc++.h>
#include <math.h>
using namespace std;
typedef long long ll;
const int N =1e6 + 10;
int a[N];
int n, m;
int st[N], ed[N], sz[N], sum[N], bel[N], mark[N];
vector<int> v[N];
inline void init(int n) { //分块
    int  sq = sqrt(n);

    for (int i = 1; i <= sq; i++) {
        st[i] = sq * (i - 1) + 1;
        ed[i] = sq * i;
        sz[i] = ed[i] - st[i] + 1;      
    }

    ed[sq] = n;
    sz[sq] = ed[sq] - st[sq] + 1;

    for (int i = 1; i <= sq; i++) {
        for (int j = st[i]; j <= ed[i]; j++) {
            bel[j] = i;
            v[i].push_back(a[j]);
             sum[i]+=a[j];
        }
        sort(v[i].begin(),v[i].end());
    }
    
}
void reset(int i){
	
	for(int j=0;j<=sz[i];j++){
		v[i][j]=a[st[i]+j];
	}
	sort(v[i].begin(),v[i].end());
}
inline void change(int l, int r, int d) { //区间修改,加上一个数
    if (bel[l] == bel[r]) {
        for (int i = l; i <= r; i++) {
            a[i] += d;
        }
        reset(bel[l]);
       
    } else {
        for (int i = l; i <= ed[bel[l]]; i++)
            a[i] += d;
        reset(bel[l]);
        
        for (int i = st[bel[r]]; i <= r; i++)
            a[i] += d;
        reset(bel[r]);
        
        for (int i = bel[l] + 1; i < bel[r]; i++)
            mark[i] += d;
    }
}
int query1(int l,int r,int c){//区间查询,小于c的个数
    int ans=0;
    if(bel[l]==bel[r]){
        for(int i=l;i<=r;i++){
            if(a[i]+mark[bel[i]]<c*c) ans++;
        }
    }else{
        for(int i=l;i<=ed[bel[l]];i++){
            if(a[i]+mark[bel[l]]<c*c) ans++;
        } 
       
        for(int i=st[bel[r]];i<=r;i++){
            if(a[i]+mark[bel[r]]<c*c) ans++;
        } 
         
        for(int i=bel[l]+1;i<bel[r];i++){
            ans+=lower_bound(v[i].begin(), v[i].end(),c*c-mark[i])-v[i].begin();
        }

    }
    return ans; 
}
int query2(int l,int r,int c){//查询区间内比x小的最大元素 
    int ans=-1;
    if(bel[l]==bel[r]){
        for(int i=l;i<=r;i++){
            if(a[i]+mark[bel[i]]<c) ans=max(ans,a[i]+mark[bel[i]]); 
        }
    }else{
        for(int i=l;i<=ed[bel[l]];i++){
            if(a[i]+mark[bel[l]]<c) ans=max(ans,a[i]+mark[bel[l]]); 
        } 
       
        for(int i=st[bel[r]];i<=r;i++){
            if(a[i]+mark[bel[r]]<c) ans=max(ans,a[i]+mark[bel[r]]); 
        } 
         
        for(int i=bel[l]+1;i<bel[r];i++){
        	int t=lower_bound(v[i].begin(), v[i].end(),c-mark[i])-v[i].begin();
        	if(t){
        		ans=max(ans,v[i][t-1]+mark[i]);
			}
        }

    }
    return ans; 
}

int main() {
    cin>>n;

    for (int i = 1; i <= n; i++)
        cin>>a[i];

    int l, r, op;
    ll c;
    init(n);

    for (int i = 1; i <= n; i++) {
        cin>>op>>l>>r>>c;

        if (op == 0) {
            change(l, r, c);
        } else {
           cout<<query2(l,r,c)<<endl;
        }
    }
}

数列分块入门 4

1.题意:

区间加法,区间求和

2.题解:

散块暴力,整块求和,求整块和时要把标记乘上块大小,同时记得i表示块th

3.ac代码:
#include <bits/stdc++.h>
#include <math.h>
using namespace std;
typedef long long ll;
const int N =1e6 + 10;
int a[N];
int n, m;
int st[N], ed[N], sz[N], sum[N], bel[N], mark[N];
vector<int> v[N];
inline void init(int n) { //分块
    int  sq = sqrt(n);

    for (int i = 1; i <= sq; i++) {
        st[i] = sq * (i - 1) + 1;
        ed[i] = sq * i;
        sz[i] = ed[i] - st[i] + 1;  
		    
    }

    ed[sq] = n;
    sz[sq] = ed[sq] - st[sq] + 1;

    for (int i = 1; i <= sq; i++) {
        for (int j = st[i]; j <= ed[i]; j++) {
            bel[j] = i;
            v[i].push_back(a[j]);
             sum[i]+=a[j];
        }
        sort(v[i].begin(),v[i].end());
    }
    
}
void reset(int i){
	
	for(int j=0;j<=sz[i];j++){
		v[i][j]=a[st[i]+j];
	}
	sort(v[i].begin(),v[i].end());
}
inline void change(int l, int r, int d) { //区间修改,加上一个数
    if (bel[l] == bel[r]) {
        for (int i = l; i <= r; i++) {
            a[i] += d;
            sum[bel[i]]+=d;
        }
        reset(bel[l]);
       
    } else {
        for (int i = l; i <= ed[bel[l]]; i++)
            a[i] += d,sum[bel[i]]+=d;
        reset(bel[l]);
        
        for (int i = st[bel[r]]; i <= r; i++)
            a[i] += d,sum[bel[i]]+=d;
        reset(bel[r]);
        
        for (int i = bel[l] + 1; i < bel[r]; i++)
            mark[i] += d;
    }
}
int query1(int l,int r,int c){//区间查询,小于c的个数
    int ans=0;
    if(bel[l]==bel[r]){
        for(int i=l;i<=r;i++){
            if(a[i]+mark[bel[i]]<c*c) ans++;
        }
    }else{
        for(int i=l;i<=ed[bel[l]];i++){
            if(a[i]+mark[bel[l]]<c*c) ans++;
        } 
       
        for(int i=st[bel[r]];i<=r;i++){
            if(a[i]+mark[bel[r]]<c*c) ans++;
        } 
         
        for(int i=bel[l]+1;i<bel[r];i++){
            ans+=lower_bound(v[i].begin(), v[i].end(),c*c-mark[i])-v[i].begin();
        }

    }
    return ans; 
}
int query2(int l,int r,int c){//查询区间内比x小的最大元素 
    int ans=-1;
    if(bel[l]==bel[r]){
        for(int i=l;i<=r;i++){
            if(a[i]+mark[bel[i]]<c) ans=max(ans,a[i]+mark[bel[i]]); 
        }
    }else{
        for(int i=l;i<=ed[bel[l]];i++){
            if(a[i]+mark[bel[l]]<c) ans=max(ans,a[i]+mark[bel[l]]); 
        } 
       
        for(int i=st[bel[r]];i<=r;i++){
            if(a[i]+mark[bel[r]]<c) ans=max(ans,a[i]+mark[bel[r]]); 
        } 
         
        for(int i=bel[l]+1;i<bel[r];i++){
        	int t=lower_bound(v[i].begin(), v[i].end(),c-mark[i])-v[i].begin();
        	if(t){
        		ans=max(ans,v[i][t-1]+mark[i]);
			}
        }

    }
    return ans; 
}
int query(int l,int r,int c){//区间和mod(c+1)
	 int ans=0;
    if(bel[l]==bel[r]){
        for(int i=l;i<=r;i++){
            ans=(a[i]+mark[bel[i]]+ans)%(c+1);
        }
    }else{
        for(int i=l;i<=ed[bel[l]];i++){
            ans=(a[i]+mark[bel[i]]+ans)%(c+1);
        } 
       
        for(int i=st[bel[r]];i<=r;i++){
             ans=(a[i]+mark[bel[i]]+ans)%(c+1);
        } 
         
        for(int i=bel[l]+1;i<bel[r];i++){
             ans=(sum[i]+(ll)sz[i]*mark[i]%(c+1)+ans)%(c+1);
        }

    }
    return ans; 
	
}
int main() {
    cin>>n;

    for (int i = 1; i <= n; i++)
        cin>>a[i];

    int l, r, op;
    ll c;
    init(n);

    for (int i = 1; i <= n; i++) {
        cin>>op>>l>>r>>c;

        if (op == 0) {
            change(l, r, c);
        } else {
           cout<<query(l,r,c)<<endl;
        }
    }
}

数列分块入门 5

1.题意:

区间开方,区间求和

2.题解:

当数字开方到1/0后,再开方数字不变,所以对整块都是0/1的块做标记,即下次不用再处理了,
开方时对零散块暴力,对整块采用标记处理
query依然对零散块暴力求解,对整块直接返回sum

3.ac代码:
#include <bits/stdc++.h>
#include <math.h>
using namespace std;
typedef long long ll;
const int N =1e6 + 10;
int a[N];
int n, m;
int st[N], ed[N], sz[N], sum[N], bel[N], mark[N];
int flag[N];
vector<int> v[N];
inline void init(int n) { //分块
    int  sq = sqrt(n);

    for (int i = 1; i <= sq; i++) {
        st[i] = sq * (i - 1) + 1;
        ed[i] = sq * i;
        sz[i] = ed[i] - st[i] + 1;  
		    
    }

    ed[sq] = n;
    sz[sq] = ed[sq] - st[sq] + 1;

    for (int i = 1; i <= sq; i++) {
        for (int j = st[i]; j <= ed[i]; j++) {
            bel[j] = i;
            v[i].push_back(a[j]);
             sum[i]+=a[j];
        }
        sort(v[i].begin(),v[i].end());
    }
    
}
void reset(int i){
	
	for(int j=0;j<=sz[i];j++){
		v[i][j]=a[st[i]+j];
	}
	sort(v[i].begin(),v[i].end());
}
inline void change(int l, int r, int d) { //区间修改,加上一个数
    if (bel[l] == bel[r]) {
        for (int i = l; i <= r; i++) {
            a[i] += d;
            sum[bel[i]]+=d;
        }
        reset(bel[l]);
       
    } else {
        for (int i = l; i <= ed[bel[l]]; i++)
            a[i] += d,sum[bel[i]]+=d;
        reset(bel[l]);
        
        for (int i = st[bel[r]]; i <= r; i++)
            a[i] += d,sum[bel[i]]+=d;
        reset(bel[r]);
        
        for (int i = bel[l] + 1; i < bel[r]; i++)
            mark[i] += d;
    }
}
int query(int l,int r,int c){//区间和mod(c+1)
	 int ans=0;
    if(bel[l]==bel[r]){
        for(int i=l;i<=r;i++){
            ans=(a[i]+mark[bel[i]]+ans)%(c+1);
        }
    }else{
        for(int i=l;i<=ed[bel[l]];i++){
            ans=(a[i]+mark[bel[i]]+ans)%(c+1);
        } 
       
        for(int i=st[bel[r]];i<=r;i++){
             ans=(a[i]+mark[bel[i]]+ans)%(c+1);
        } 
         
        for(int i=bel[l]+1;i<bel[r];i++){
             ans=(sum[i]+(ll)sz[i]*mark[i]%(c+1)+ans)%(c+1);
        }

    }
    return ans; 
	
}
int query1(int l,int r,int c){//区间查询,小于c的个数
    int ans=0;
    if(bel[l]==bel[r]){
        for(int i=l;i<=r;i++){
            if(a[i]+mark[bel[i]]<c*c) ans++;
        }
    }else{
        for(int i=l;i<=ed[bel[l]];i++){
            if(a[i]+mark[bel[l]]<c*c) ans++;
        } 
       
        for(int i=st[bel[r]];i<=r;i++){
            if(a[i]+mark[bel[r]]<c*c) ans++;
        } 
         
        for(int i=bel[l]+1;i<bel[r];i++){
            ans+=lower_bound(v[i].begin(), v[i].end(),c*c-mark[i])-v[i].begin();
        }

    }
    return ans; 
}
int query2(int l,int r,int c){//查询区间内比x小的最大元素 
    int ans=-1;
    if(bel[l]==bel[r]){
        for(int i=l;i<=r;i++){
            if(a[i]+mark[bel[i]]<c) ans=max(ans,a[i]+mark[bel[i]]); 
        }
    }else{
        for(int i=l;i<=ed[bel[l]];i++){
            if(a[i]+mark[bel[l]]<c) ans=max(ans,a[i]+mark[bel[l]]); 
        } 
       
        for(int i=st[bel[r]];i<=r;i++){
            if(a[i]+mark[bel[r]]<c) ans=max(ans,a[i]+mark[bel[r]]); 
        } 
         
        for(int i=bel[l]+1;i<bel[r];i++){
        	int t=lower_bound(v[i].begin(), v[i].end(),c-mark[i])-v[i].begin();
        	if(t){
        		ans=max(ans,v[i][t-1]+mark[i]);
			}
        }

    }
    return ans; 
}
int query3(int l,int r){//区间开方的求和 
	int ans=0;
    if(bel[l]==bel[r]){
        for(int i=l;i<=r;i++){
            ans+=a[i]+mark[bel[i]];
        }
    }else{
        for(int i=l;i<=ed[bel[l]];i++){
            ans+=a[i]+mark[bel[i]];
        } 
       
        for(int i=st[bel[r]];i<=r;i++){
             ans+=a[i]+mark[bel[i]];
        } 
         
        for(int i=bel[l]+1;i<bel[r];i++){
             ans+=sum[i];
        }

    }
    return ans; 
	
}
void solve_sqrt(int x){//对整块开放后的区间处理sum 
	if(flag[x]) return;//flag!=0即整块不用处理 
	flag[x]=1;
	sum[x]=0;
	for(int i=st[x];i<=ed[x];i++){
		a[i]=sqrt(a[i]);
		sum[x]+=a[i];
		if(a[i]>1) flag[x]=0;
	}
}
void changeSqrt(int l,int r){//区间开方 
	if (bel[l] == bel[r]) {
        for (int i = l; i <= r; i++) {
        	sum[bel[i]]-=a[i];
            a[i] =sqrt(a[i]);
            sum[bel[i]]+=a[i];
        }
      
       
    } else {
        for (int i = l; i <= ed[bel[l]]; i++){
        	sum[bel[i]]-=a[i];
            a[i] =sqrt(a[i]);
            sum[bel[i]]+=a[i];
		}
           
        for (int i = st[bel[r]]; i <= r; i++){
        	sum[bel[i]]-=a[i];
            a[i] =sqrt(a[i]);
            sum[bel[i]]+=a[i];
		}
      
        for (int i = bel[l] + 1; i < bel[r]; i++){
        	solve_sqrt(i);
		}
    }
} 

int main() {
    cin>>n;

    for (int i = 1; i <= n; i++)
        cin>>a[i];

    int l, r, op;
    ll c;
    init(n);

    for (int i = 1; i <= n; i++) {
        cin>>op>>l>>r>>c;

        if (op == 0) {
            changeSqrt(l, r);
        } else {
           cout<<query3(l,r)<<endl;
        }
    }
}

数列分块入门 6

1.题意:

单点插入,单点询问,数据随机生成

2.题解:

关键是rebuild,当增加的数值达到一个块的大小时,就重新分块
开始一直wa是因为,写之前的题里头,把v排序了

3.ac代码:
#include <bits/stdc++.h>
#include <math.h>
using namespace std;
typedef long long ll;
const int N =1e6 + 10;
int a[N];
int n, m;
int sq;
int st[N], ed[N], sz[N], sum[N], bel[N], mark[N];
int flag[N];
vector<int> v[N];
inline void init(int n) { //分块
     sq = sqrt(n);

    for (int i = 1; i <= sq; i++) {
        st[i] = sq * (i - 1) + 1;
        ed[i] = sq * i;
        sz[i] = ed[i] - st[i] + 1;  
		    
    }

    ed[sq] = n;
    sz[sq] = ed[sq] - st[sq] + 1;

    for (int i = 1; i <= sq; i++) {
        for (int j = st[i]; j <= ed[i]; j++) {
            bel[j] = i;
            v[i].push_back(a[j]);
             sum[i]+=a[j];
        }
       // sort(v[i].begin(),v[i].end());
    }
    
}
void reset(int i){
	
	for(int j=0;j<=sz[i];j++){
		v[i][j]=a[st[i]+j];
	}
	sort(v[i].begin(),v[i].end());
}
void rebuild(){//重新分块 
    int k=0;
	for(int i=1;i<=sq;i++){
        for(int j=0;j<v[i].size();j++){
            a[++k]=v[i][j];
        }
        v[i].clear();
    }
    sq=sqrt(k);

    
    for(int i=1;i<=k;i++){
      
        v[(i-1)/sq+1].push_back(a[i]);
    }
} 
inline void change(int l, int r, int d) { //区间修改,加上一个数

    if (bel[l] == bel[r]) {
        for (int i = l; i <= r; i++) {
            a[i] += d;
            sum[bel[i]]+=d;
        }
        reset(bel[l]);
       
    } else {
        for (int i = l; i <= ed[bel[l]]; i++)
            a[i] += d,sum[bel[i]]+=d;
        reset(bel[l]);
        
        for (int i = st[bel[r]]; i <= r; i++)
            a[i] += d,sum[bel[i]]+=d;
        reset(bel[r]);
        
        for (int i = bel[l] + 1; i < bel[r]; i++)
            mark[i] += d;
    }
}
pair<int,int> query4(int x){
	 int i=1;
	while(x>v[i].size()){
        x-=v[i].size();
        i++;
    }
    return make_pair(i,x-1);
}
void insert(int l,int r){//单点插入,在第l个数字前插入r 
	 auto t=query4(l);
    v[t.first].insert(v[t.first].begin()+t.second, r);
   
}
int query(int l,int r,int c){//区间和mod(c+1)
	 int ans=0;
    if(bel[l]==bel[r]){
        for(int i=l;i<=r;i++){
            ans=(a[i]+mark[bel[i]]+ans)%(c+1);
        }
    }else{
        for(int i=l;i<=ed[bel[l]];i++){
            ans=(a[i]+mark[bel[i]]+ans)%(c+1);
        } 
       
        for(int i=st[bel[r]];i<=r;i++){
             ans=(a[i]+mark[bel[i]]+ans)%(c+1);
        } 
         
        for(int i=bel[l]+1;i<bel[r];i++){
             ans=(sum[i]+(ll)sz[i]*mark[i]%(c+1)+ans)%(c+1);
        }

    }
    return ans; 
	
}
int query1(int l,int r,int c){//区间查询,小于c的个数
    int ans=0;
    if(bel[l]==bel[r]){
        for(int i=l;i<=r;i++){
            if(a[i]+mark[bel[i]]<c*c) ans++;
        }
    }else{
        for(int i=l;i<=ed[bel[l]];i++){
            if(a[i]+mark[bel[l]]<c*c) ans++;
        } 
       
        for(int i=st[bel[r]];i<=r;i++){
            if(a[i]+mark[bel[r]]<c*c) ans++;
        } 
         
        for(int i=bel[l]+1;i<bel[r];i++){
            ans+=lower_bound(v[i].begin(), v[i].end(),c*c-mark[i])-v[i].begin();
        }

    }
    return ans; 
}
int query2(int l,int r,int c){//查询区间内比x小的最大元素 
    int ans=-1;
    if(bel[l]==bel[r]){
        for(int i=l;i<=r;i++){
            if(a[i]+mark[bel[i]]<c) ans=max(ans,a[i]+mark[bel[i]]); 
        }
    }else{
        for(int i=l;i<=ed[bel[l]];i++){
            if(a[i]+mark[bel[l]]<c) ans=max(ans,a[i]+mark[bel[l]]); 
        } 
       
        for(int i=st[bel[r]];i<=r;i++){
            if(a[i]+mark[bel[r]]<c) ans=max(ans,a[i]+mark[bel[r]]); 
        } 
         
        for(int i=bel[l]+1;i<bel[r];i++){
        	int t=lower_bound(v[i].begin(), v[i].end(),c-mark[i])-v[i].begin();
        	if(t){
        		ans=max(ans,v[i][t-1]+mark[i]);
			}
        }

    }
    return ans; 
}
int query3(int l,int r){//区间开方的求和 
	int ans=0;
    if(bel[l]==bel[r]){
        for(int i=l;i<=r;i++){
            ans+=a[i]+mark[bel[i]];
        }
    }else{
        for(int i=l;i<=ed[bel[l]];i++){
            ans+=a[i]+mark[bel[i]];
        } 
       
        for(int i=st[bel[r]];i<=r;i++){
             ans+=a[i]+mark[bel[i]];
        } 
         
        for(int i=bel[l]+1;i<bel[r];i++){
             ans+=sum[i];
        }

    }
    return ans; 
	
}

void solve_sqrt(int x){//对整块开放后的区间处理sum 
	if(flag[x]) return;//flag!=0即整块不用处理 
	flag[x]=1;
	sum[x]=0;
	for(int i=st[x];i<=ed[x];i++){
		a[i]=sqrt(a[i]);
		sum[x]+=a[i];
		if(a[i]>1) flag[x]=0;
	}
}
void changeSqrt(int l,int r){//区间开方 
	if (bel[l] == bel[r]) {
        for (int i = l; i <= r; i++) {
        	sum[bel[i]]-=a[i];
            a[i] =sqrt(a[i]);
            sum[bel[i]]+=a[i];
        }
      
       
    } else {
        for (int i = l; i <= ed[bel[l]]; i++){
        	sum[bel[i]]-=a[i];
            a[i] =sqrt(a[i]);
            sum[bel[i]]+=a[i];
		}
           
        for (int i = st[bel[r]]; i <= r; i++){
        	sum[bel[i]]-=a[i];
            a[i] =sqrt(a[i]);
            sum[bel[i]]+=a[i];
		}
      
        for (int i = bel[l] + 1; i < bel[r]; i++){
        	solve_sqrt(i);
		}
    }
} 

int main() {
    cin>>n;

    for (int i = 1; i <= n; i++)
        cin>>a[i];

    int l, r, op;
    ll c;
    sq=sqrt(n);

     init(n);
    int cnt=0;

    for (int i = 1; i <= n; i++) {
        cin>>op>>l>>r>>c;

        if (op == 0) {
            insert(l,r);cnt++;
        } else {
        	auto t=query4(r);
           cout<<v[t.first][t.second]<<endl;
        }
        if(cnt==sq){
        	rebuild();
        	cnt=0;
		}
    }
}

数列分块入门 7

1.题意:

区间乘法,区间加法,单点询问

2.题解:

本质上和区间加法差不多,更新零散块后再暴力,对整块做整块标记处理。

3.ac代码:
#include <bits/stdc++.h>
#include <math.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
const int mod = 10007;
ll a[N];
int n, m;
int sq;
int st[N], ed[N], sz[N], sum[N], bel[N];
int flag[N];
ll multi[N], mark[N];
vector<int> v[N];
inline void init(int n) { //分块
    sq = sqrt(n);

    for (int i = 1; i <= sq; i++) {
        st[i] = sq * (i - 1) + 1;
        ed[i] = sq * i;
        sz[i] = ed[i] - st[i] + 1;

    }

    ed[sq] = n;
    sz[sq] = ed[sq] - st[sq] + 1;

    for (int i = 1; i <= sq; i++) {
        multi[i] = 1;

        for (int j = st[i]; j <= ed[i]; j++) {
            bel[j] = i;
            v[i].push_back(a[j]);
            sum[i] += a[j];
        }

        // sort(v[i].begin(),v[i].end());
    }

}
void reset(int i) { //排序v

    for (int j = 0; j <= sz[i]; j++) {
        v[i][j] = a[st[i] + j];
    }

    sort(v[i].begin(), v[i].end());
}
void rebuild() { //重新分块
    int k = 0;

    for (int i = 1; i <= sq; i++) {
        for (int j = 0; j < v[i].size(); j++) {
            a[++k] = v[i][j];
        }

        v[i].clear();
    }

    sq = sqrt(k);


    for (int i = 1; i <= k; i++) {

        v[(i - 1) / sq + 1].push_back(a[i]);
    }
}
inline void change(int l, int r, int d) { //区间修改,加上一个数

    if (bel[l] == bel[r]) {
        for (int i = l; i <= r; i++) {
            a[i] += d;
            sum[bel[i]] += d;
        }

        reset(bel[l]);

    } else {
        for (int i = l; i <= ed[bel[l]]; i++)
            a[i] += d, sum[bel[i]] += d;

        reset(bel[l]);

        for (int i = st[bel[r]]; i <= r; i++)
            a[i] += d, sum[bel[i]] += d;

        reset(bel[r]);

        for (int i = bel[l] + 1; i < bel[r]; i++)
            mark[i] += d;
    }
}
pair<int, int> query4(int x) {
    int i = 1;

    while (x > v[i].size()) {
        x -= v[i].size();
        i++;
    }

    return make_pair(i, x - 1);
}
//void insert(int l, int r) { //单点插入,在第l个数字前插入r
//    auto t = query4(l);
//    v[t.first].insert(v[t.first].begin() + t.second, r);
//
//}
int query(int l, int r, int c) { //区间和mod(c+1)
    int ans = 0;

    if (bel[l] == bel[r]) {
        for (int i = l; i <= r; i++) {
            ans = (a[i] + mark[bel[i]] + ans) % (c + 1);
        }
    } else {
        for (int i = l; i <= ed[bel[l]]; i++) {
            ans = (a[i] + mark[bel[i]] + ans) % (c + 1);
        }

        for (int i = st[bel[r]]; i <= r; i++) {
            ans = (a[i] + mark[bel[i]] + ans) % (c + 1);
        }

        for (int i = bel[l] + 1; i < bel[r]; i++) {
            ans = (sum[i] + (ll)sz[i] * mark[i] % (c + 1) + ans) % (c + 1);
        }

    }

    return ans;

}
int query1(int l, int r, int c) { //区间查询,小于c的个数
    int ans = 0;

    if (bel[l] == bel[r]) {
        for (int i = l; i <= r; i++) {
            if (a[i] + mark[bel[i]] < c * c)
                ans++;
        }
    } else {
        for (int i = l; i <= ed[bel[l]]; i++) {
            if (a[i] + mark[bel[l]] < c * c)
                ans++;
        }

        for (int i = st[bel[r]]; i <= r; i++) {
            if (a[i] + mark[bel[r]] < c * c)
                ans++;
        }

        for (int i = bel[l] + 1; i < bel[r]; i++) {
            ans += lower_bound(v[i].begin(), v[i].end(), c * c - mark[i]) - v[i].begin();
        }

    }

    return ans;
}

int query3(int l, int r) { //区间开方的求和
    int ans = 0;

    if (bel[l] == bel[r]) {
        for (int i = l; i <= r; i++) {
            ans += a[i] + mark[bel[i]];
        }
    } else {
        for (int i = l; i <= ed[bel[l]]; i++) {
            ans += a[i] + mark[bel[i]];
        }

        for (int i = st[bel[r]]; i <= r; i++) {
            ans += a[i] + mark[bel[i]];
        }

        for (int i = bel[l] + 1; i < bel[r]; i++) {
            ans += sum[i];
        }

    }

    return ans;

}

void solve_sqrt(int x) { //对整块开放后的区间处理sum
    if (flag[x])
        return;//flag!=0即整块不用处理

    flag[x] = 1;
    sum[x] = 0;

    for (int i = st[x]; i <= ed[x]; i++) {
        a[i] = sqrt(a[i]);
        sum[x] += a[i];

        if (a[i] > 1)
            flag[x] = 0;
    }
}
void changeSqrt(int l, int r) { //区间开方
    if (bel[l] == bel[r]) {
        for (int i = l; i <= r; i++) {
            sum[bel[i]] -= a[i];
            a[i] = sqrt(a[i]);
            sum[bel[i]] += a[i];
        }


    } else {
        for (int i = l; i <= ed[bel[l]]; i++) {
            sum[bel[i]] -= a[i];
            a[i] = sqrt(a[i]);
            sum[bel[i]] += a[i];
        }

        for (int i = st[bel[r]]; i <= r; i++) {
            sum[bel[i]] -= a[i];
            a[i] = sqrt(a[i]);
            sum[bel[i]] += a[i];
        }

        for (int i = bel[l] + 1; i < bel[r]; i++) {
            solve_sqrt(i);
        }
    }
}
void update(int l) {

    for (int i = st[l]; i <= ed[l]; i++) {
        a[i] = (multi[l] * a[i]%mod + mark[l]) % mod;
    }

    multi[l] = 1;
    mark[l] = 0;
}
void change(int op, int l, int r, int d) { //乘法,加法
    update(bel[l]);
    

    if (op == 0) {
        if (bel[l] == bel[r]) {
            for (int i = l; i <= r; i++) {
                a[i]=(a[i]+d)%mod;
            }
        } else {
        	update(bel[r]);
            for (int i = l; i <= ed[bel[l]]; i++)
               a[i]=(a[i]+d)%mod;

            for (int i = st[bel[r]]; i <= r; i++)
                a[i]=(a[i]+d)%mod;

            for (int i = bel[l] + 1; i < bel[r]; i++)
                mark[i]=(mark[i]+d)%mod;
        }

    } else {
        if (bel[l] == bel[r]) {
            for (int i = l; i <= r; i++) {
                a[i] = a[i] *d % mod;
            }
        } else {
        	update(bel[r]);
            for (int i = l; i <= ed[bel[l]]; i++)
                 a[i] = a[i] *d % mod;

            for (int i = st[bel[r]]; i <= r; i++)
                 a[i] = a[i] *d % mod;

            for (int i = bel[l] + 1; i < bel[r]; i++)
                multi[i]= d *multi[i] % mod,mark[i]=mark[i]*d%mod;//!!!切记乘法 对加法标记也要乘上去 
        }

    }
}
int main() {
    cin >> n;

    for (int i = 1; i <= n; i++)
        cin >> a[i];


    int l, r, op;
    ll c;
    sq = sqrt(n);

    init(n);

    for (int i = 1; i <= n; i++) {
        cin >> op >> l >> r >> c;

        if (op == 2) {
            cout << (multi[bel[r]]*a[r]%mod + mark[bel[r]]) % mod << endl;
        } else {
            change(op, l, r, c);
        }

    }
}

数列分块入门 8

1.题意:

操作涉及区间询问等于一个数 的元素,并将这个区间的所有元素改为 。

2.题解:

整块变化则将整块标记,跟前面的题类似
易错:1.传参传的是组号,不是区间短点,2.注意参数到底是谁

3.ac代码:
#include <bits/stdc++.h>
#include <math.h>
using namespace std;
typedef long long ll;
const int N = 2e5 + 10;

const int INF=0x3f3f3f3f;
ll a[N];
int n, m;
int sq;
int st[N], ed[N], sz[N], sum[N], bel[N];
int num[N];
ll multi[N], mark[N];
vector<int> v[N];
inline void init(int n) { //分块
    sq = sqrt(n);

    for (int i = 1; i <= sq; i++) {
        st[i] = sq * (i - 1) + 1;
        ed[i] = sq * i;
        sz[i] = ed[i] - st[i] + 1;

    }

    ed[sq] = n;
    sz[sq] = ed[sq] - st[sq] + 1;

    for (int i = 1; i <= sq; i++) {
        multi[i] = 1;

        for (int j = st[i]; j <= ed[i]; j++) {
            bel[j] = i;
            v[i].push_back(a[j]);
            sum[i] += a[j];
        }

        // sort(v[i].begin(),v[i].end());
    }
    

}







void update1(int x){
	if(num[x]==-1) return;
	for(int i=st[x];i<=ed[x];i++){
		a[i]=num[x];
	}
	num[x]=-1;
}


//void change(int op, int l, int r, int d) { //乘法,加法

int query4(int l,int r,int c){//查询区间中c的个数,并把区间所有数组改成c 
	int sum=0;
	update1(bel[l]);
	if(bel[l]==bel[r]){
		for(int i=l;i<=r;i++){
			if(a[i]==c) sum++;
			else a[i]=c;
		}
	}else{
		for(int i=l;i<=ed[bel[l]];i++){
			if(a[i]==c) sum++;
			else a[i]=c;
		}
		update1(bel[r]);
		for(int i=st[bel[r]];i<=r;i++){
			if(a[i]==c) sum++;
			else a[i]=c;
		}
		for(int i=bel[l]+1;i<bel[r];i++){
			if(num[i]==-1){
				for(int j=st[i];j<=ed[i];j++){
					if(a[j]==c) sum++;
					else a[j]=c;
				}
				num[i]=c;
			}else{
				if(num[i]==c) sum+=sz[i]; 
				num[i]=c;
			}
		}
		
	}
	return sum;
}
int main() {
    cin >> n;
    memset(num,-1,sizeof num);

    for (int i = 1; i <= n; i++){
    	cin >> a[i];
    	
	}
        


    int l, r, op;
    ll c;
    sq = sqrt(n);

    init(n);

    for (int i = 1; i <= n; i++) {
        cin  >> l >> r >> c;
        cout<<query4(l,r,c)<<endl;
        

    

    }
}

数列分块入门 9

1.题意:

离线的区间众数

2.题解:

!!!太好了啊啊啊啊啊啊啊啊啊啊啊啊,卡了我一下午,要不t要不wa要不re,终于终于!!
一开始错在,考虑了每个分块的众数,结果没有考虑几个块合成大块的众数
后来又是re,re,开始一直以为是数组越界,后来发现好像是数字太大了,不能z直接通过桶排计算,然后看别人代码学做了离散化
离散化后依旧re,然后把分块大小从√n变成了80
好了现在又是t了,不过好歹是80+,经过不断的改代码,我把map变成了普通的数组cnt,ookk终于过了!!,一个map让我超时了好久,我估计我用的pair也给我加了不少时,呜呜呜,我以后还老老实实用普通数组吧

总结下,这题基本思路:初始化求出不同块的众数+离散化+分块大小80+少用map等
历时几天的分块九题终于完了!!!撒花,感谢这位博主题解写的好!!

这题好像还能用莫队,回头试试

3.ac代码:
#include <bits/stdc++.h>
#include <math.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
int f[5003][5003];
const int INF = 0x3f3f3f3f;
int a[N];
int n;
int sq;
int st[N], ed[N], sz[N], sum[N], bel[N];
int num[N];
ll multi[N], mark[N];
int cnt[N];
vector<int> v[N];
map<int, int> m;
int dis[N];
int count(int x, int l, int r) {
    return upper_bound(v[x].begin(), v[x].end(), r) - lower_bound(v[x].begin(), v[x].end(), l);
}

inline void init(int n) { //分块

    int val = 80;
    sq = (n - 1) / val + 1;


    for (int i = 1; i <= sq; i++) {
        st[i] = val * (i - 1) + 1;
        ed[i] = val * i;
        sz[i] = ed[i] - st[i] + 1;

    }

    ed[sq] = n;
    sz[sq] = ed[sq] - st[sq] + 1;

    for (int i = 1; i <= sq; i++) {
        for (int j = st[i]; j <= ed[i]; j++) {
            bel[j] = i;

        }
    }

    for (int i = 1; i <= sq; i++) {
        int maxx = INF, maxn = 0;
        memset(cnt, 0, sizeof cnt);

        for (int k = i; k <= sq; k++) {
            for (int j = st[k]; j <= ed[k]; j++) {

                cnt[a[j]]++;

                if (cnt[a[j]] > maxn) {
                    maxn = cnt[a[j]];
                    maxx = a[j];
                } else if (cnt[a[j]] == maxn) {
                    maxx = min(maxx, a[j]);
                }

            }

            f[i][k] = maxx;
        }


    }


}
pair<int, int> query5(int l, int r) {
    int ans = 0, ansx;
    pair<int, int> temp;
    temp = {INF, 0};

    if (bel[l] == bel[r]) {
        memset(cnt, 0, sizeof cnt);

        for (int i = l; i <= r; i++) {
            cnt[a[i]]++;

            if (cnt[a[i]] > temp.second) {
                temp = {a[i], cnt[a[i]]};
            } else if (cnt[a[i]] == temp.second) {
                temp.first = min(temp.first, a[i]);
            }
        }
    } else {

        int tot;

        for (int i = l; i <= ed[bel[l]]; i++) {
            tot = count(a[i], l, r);

            if (tot > temp.second) {
                temp = {a[i], tot};
            } else if (tot == temp.second) {
                temp.first = min(temp.first, a[i]);
            }
        }

        for (int i = st[bel[r]]; i <= r; i++) {
            tot = count(a[i], l, r);

            if (tot > temp.second) {
                temp = {a[i], tot};
            } else if (tot == temp.second) {
                temp.first = min(temp.first, a[i]);
            }
        }

        if (bel[l] + 1 < bel[r]) {
            int t = f[bel[l] + 1][bel[r] - 1];
            tot = count(t, l, r);

            if (tot > temp.second) {
                temp = {t, tot};
            } else if (tot == temp.second) {
                temp.first = min(temp.first, t);
            }
        }


    }

    return temp;
}
int main() {
    cin >> n;
    memset(num, -1, sizeof num);

    for (int i = 1; i <= n; i++) {
        cin >> a[i];
        dis[++dis[0]] = a[i];
    }

    sort(dis + 1, dis + dis[0] + 1);

    for (int i = 1; i <= dis[0]; i++) {
        m[dis[i]] = i;
    }

    for (int i = 1; i <= n; i++) {
        a[i] = m[a[i]];
    }

    for (int i = 1; i <= n; i++) {
        v[a[i]].push_back(i);
    }



    int l, r, op;
    ll c;
    sq = sqrt(n);

    init(n);

    for (int i = 1; i <= n; i++) {
        cin  >> l >> r ;
        cout << dis[query5(l, r).first] << endl;




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值