数列分块入门 Libre OJ #6277~~6285

/**
数列分块:http://hzwer.com/8053.html

数列分块 1 :
链接:https://loj.ac/problem/6277
区间加法+单点查询

分块:块的大小 (len): sqrt(n);
对每个数属于第几块  pos[i] = (i-1) / sqrt(n) + 1;
对于一段序列 一般分为三个部分  前半块  中间完整块 后半块;
由于每个块的长度都是   <=sqrt(n) 因此更新操作也是3*sqrt(n);

对于完整的块  可以直接更新到tag数组里面
表示当前块中元素整体加上val
tag[i]:加法标记数组  表示第i块加上了tag[i];

对于不完整的块  由于元素个数较少 可直接一个暴力修改

时间复杂度:n*sqrt(n);
*/


#include<bits/stdc++.h>
#define ll long long
using namespace std;
/**********************************************Head-----Template****************************************/
bool Finish_read;
template<class T>inline void read(T &x){Finish_read=0;x=0;int f=1;char ch=getchar();while(!isdigit(ch)){if(ch=='-')f=-1;if(ch==EOF)return;ch=getchar();}while(isdigit(ch))x=x*10+ch-'0',ch=getchar();x*=f;Finish_read=1;}
template<class T>inline void print(T x){if(x/10!=0)print(x/10);putchar(x%10+'0');}
template<class T>inline void writeln(T x){if(x<0)putchar('-');x=abs(x);print(x);putchar('\n');}
template<class T>inline void write(T x){if(x<0)putchar('-');x=abs(x);print(x);}
ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);}
ll lcm(ll a,ll b){ll gg=gcd(a,b);a/=gg;if(a<=LLONG_MAX/b) return a*b;return LLONG_MAX;}
/********************************Head----Temlate**********************************************/


const int maxn=1e5+7;
int n,m;
int s[maxn],tag[maxn],pos[maxn];

void change(int l,int r,int val){
    for(int i=l;i<=min(r,pos[l]*m);i++) s[i]+=val;
    if(pos[l]!=pos[r])
        for(int i=(pos[r]-1)*m+1;i<=r;i++) s[i]+=val;
    for(int i=pos[l]+1;i<=pos[r]-1;i++) tag[i]+=val;
}

void solved(){
    read(n);m=sqrt(n);
    for(int i=1;i<=n;i++) pos[i]=(i-1)/m+1;
    for(int i=1;i<=n;i++) read(s[i]);
    for(int i=1;i<=n;i++){
        int ch,l,r,val;
        read(ch),read(l),read(r),read(val);
        if(ch==0) change(l,r,val);
        else writeln((tag[pos[r]]+s[r]));
    }
}

int main(){
    solved();
    return 0;
}

/**
数列分块2:
链接:https://loj.ac/problem/6278

区间加法+区间计数(区间数值小于val的个数);

区间加法:
套用第一题的方法 维护一个加法标记数组
略微不同的是:

对于不完整的块
对其进行更新的时候当前块不一定是有序的;
因此 需要对当前块重新进行排序
时间复杂度:sqrt(n)+sqrt(n)*log(sqrt*(n));

对于完整的块来说 由于 v[i]: 存储的是第i块所有元素的值 且有序
因此 可通过二分进行查找(val-tag[i])的元素值的个数

询问操作时间复杂度: n*(sqrt(n)+sqrt(n)*log(sqrt(n)) 常数较大;
*/

#include<bits/stdc++.h>
#define ll long long
using namespace std;
/**********************************************Head-----Template****************************************/
bool Finish_read;
template<class T>inline void read(T &x){Finish_read=0;x=0;int f=1;char ch=getchar();while(!isdigit(ch)){if(ch=='-')f=-1;if(ch==EOF)return;ch=getchar();}while(isdigit(ch))x=x*10+ch-'0',ch=getchar();x*=f;Finish_read=1;}
template<class T>inline void print(T x){if(x/10!=0)print(x/10);putchar(x%10+'0');}
template<class T>inline void writeln(T x){if(x<0)putchar('-');x=abs(x);print(x);putchar('\n');}
template<class T>inline void write(T x){if(x<0)putchar('-');x=abs(x);print(x);}
ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);}
ll lcm(ll a,ll b){ll gg=gcd(a,b);a/=gg;if(a<=LLONG_MAX/b) return a*b;return LLONG_MAX;}
/********************************Head----Temlate**********************************************/

const int maxn=1e5+7;
int n,m;
int s[maxn],tag[maxn],pos[maxn];
vector<int>v[maxn];

void reset(int x){//不完整块直接对v[x]进行更新
    v[x].clear();
    for(int i=(x-1)*m+1;i<=min(x*m,n);i++)
        v[x].push_back(s[i]);
    sort(v[x].begin(),v[x].end());
}

void change(int l,int r,int val){
    for(int i=l;i<=min(r,pos[l]*m);i++) s[i]+=val;//可能为最后一个块的元素 min一下

    reset(pos[l]);

    if(pos[l]!=pos[r]){
        for(int i=(pos[r]-1)*m+1;i<=r;i++) s[i]+=val;
        reset(pos[r]);
    }

    for(int i=pos[l]+1;i<=pos[r]-1;i++) tag[i]+=val;
}

int cal(int l,int r,int val){
    int ans=0;
    for(int i=l;i<=min(r,pos[l]*m);i++) if(tag[pos[l]]+s[i]<val) ans++;
    if(pos[l]!=pos[r]){
        for(int i=(pos[r]-1)*m+1;i<=r;i++)
            if(tag[pos[r]]+s[i]<val) ans++;
    }
    for(int i=pos[l]+1;i<=pos[r]-1;i++){
        int tmp=val-tag[i];//二分查找第i块中小于等于val-tag[i]的值;
        ans+=lower_bound(v[i].begin(),v[i].end(),tmp)-v[i].begin();
    }
    return ans;
}


void solved(){
    read(n);m=sqrt(n);//块的大小;
    for(int i=1;i<=n;i++) read(s[i]);
    for(int i=1;i<=n;i++) {
        pos[i]=(i-1)/m+1;
        v[pos[i]].push_back(s[i]);
    }
    for(int i=1;i<=pos[n];i++)
        sort(v[i].begin(),v[i].end());
    for(int i=1;i<=n;i++){
        int ch,l,r,val;
        read(ch),read(l),read(r),read(val);
        if(ch==0) change(l,r,val);
        else writeln(cal(l,r,val*val));
    }
}

int main(){
    solved();
    return 0;
}

/**
数列分块3
链接:https://loj.ac/problem/6279
vector 耗用时间:3929ms
区间加法 + 询问区间x的前驱(比其小的最大元素)
和上题一样 只不过是在计数时不断更新最大值即可;
询问操作时间复杂度: n*(sqrt(n)+sqrt(n)*log(sqrt(n)) 常数较大;
*/

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

/**********************************************Head-----Template****************************************/
bool Finish_read;
template<class T>inline void read(T &x){Finish_read=0;x=0;int f=1;char ch=getchar();while(!isdigit(ch)){if(ch=='-')f=-1;if(ch==EOF)return;ch=getchar();}while(isdigit(ch))x=x*10+ch-'0',ch=getchar();x*=f;Finish_read=1;}
template<class T>inline void print(T x){if(x/10!=0)print(x/10);putchar(x%10+'0');}
template<class T>inline void writeln(T x){if(x<0)putchar('-');x=abs(x);print(x);putchar('\n');}
template<class T>inline void write(T x){if(x<0)putchar('-');x=abs(x);print(x);}
ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);}
ll lcm(ll a,ll b){ll gg=gcd(a,b);a/=gg;if(a<=LLONG_MAX/b) return a*b;return LLONG_MAX;}
/********************************Head----Temlate**********************************************/

const int maxn=1e5+7;
int n,m;
int s[maxn],tag[maxn],pos[maxn];
vector<int>v[maxn];//有序的块 块内元素s[i];

void reset(int x){
    v[x].clear();
    for(int i=(x-1)*m+1;i<=min(x*m,n);i++)
        v[x].push_back(s[i]);
    sort(v[x].begin(),v[x].end());
}

void change(int l,int r,int val){
    for(int i=l;i<=min(r,pos[l]*m);i++) s[i]+=val;    //  l 不完整块 元素
    reset(pos[l]);
    if(pos[l]!=pos[r]){
        for(int i=(pos[r]-1)*m+1;i<=r;i++) s[i]+=val; //  r 不完整块 元素
        reset(pos[r]);
    }
    for(int i=pos[l]+1;i<=pos[r]-1;i++) tag[i]+=val;  //  mid
}

int cal(int l,int r,int val){
    int ans=-1;

    for(int i=l;i<=min(r,pos[l]*m);i++)
        if(tag[pos[l]]+s[i]<val) ans=max(ans,s[i]+tag[pos[l]]);//不断更新前驱的最大值

    if(pos[l]!=pos[r]){
        for(int i=(pos[r]-1)*m+1;i<=r;i++)
            if(tag[pos[r]]+s[i]<val) ans=max(ans,s[i]+tag[pos[r]]);
    }

    for(int i=pos[l]+1;i<=pos[r]-1;i++){
        int cnt=lower_bound(v[i].begin(),v[i].end(),val-tag[i])-v[i].begin();//扎到第一个大于等于val-tag[i]的数;
        if(cnt>0) ans=max(ans,v[i][cnt-1]+tag[i]);
    }
    return ans;
}


void solved(){
    read(n);m=sqrt(n);//块的大小;
    for(int i=1;i<=n;i++) read(s[i]);
    for(int i=1;i<=n;i++) {
        pos[i]=(i-1)/m+1;
        v[pos[i]].push_back(s[i]);
    }
    for(int i=1;i<=pos[n];i++)
        sort(v[i].begin(),v[i].end());
    for(int i=1;i<=n;i++){
        int ch,l,r,val;
        read(ch),read(l),read(r),read(val);
        if(ch==0) change(l,r,val);
        else writeln(cal(l,r,val));
    }
}

int main(){
    solved();
    return 0;
}

/**
数列分块 3
set 时间: 7645 ms
想比较vector 而言 ummm 慢得不谈;
不完整块  直接清空 reset 想法类似;;;
*/

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

/**********************************************Head-----Template****************************************/
bool Finish_read;
template<class T>inline void read(T &x){Finish_read=0;x=0;int f=1;char ch=getchar();while(!isdigit(ch)){if(ch=='-')f=-1;if(ch==EOF)return;ch=getchar();}while(isdigit(ch))x=x*10+ch-'0',ch=getchar();x*=f;Finish_read=1;}
template<class T>inline void print(T x){if(x/10!=0)print(x/10);putchar(x%10+'0');}
template<class T>inline void writeln(T x){if(x<0)putchar('-');x=abs(x);print(x);putchar('\n');}
template<class T>inline void write(T x){if(x<0)putchar('-');x=abs(x);print(x);}
ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);}
ll lcm(ll a,ll b){ll gg=gcd(a,b);a/=gg;if(a<=LLONG_MAX/b) return a*b;return LLONG_MAX;}
/********************************Head----Temlate**********************************************/

const int maxn=1e5+7;
int n,m;
int s[maxn],tag[maxn],pos[maxn];
set<int>st[maxn];//有序的块 块内元素s[i];

void change(int l,int r,int val){
    st[pos[l]].clear();
    for(int i=l;i<=min(r,pos[l]*m);i++) s[i]+=val;
    for(int i=(pos[l]-1)*m+1;i<=min(pos[l]*m,n);i++) st[pos[l]].insert(s[i]);

    if(pos[l]!=pos[r]){
        st[pos[r]].clear();
        for(int i=(pos[r]-1)*m+1;i<=r;i++) s[i]+=val;
        for(int i=(pos[r]-1)*m+1;i<=max(r,min(n,pos[r]*m));i++) st[pos[r]].insert(s[i]);//边界处理需谨慎,真尼玛操蛋
    }
    for(int i=pos[l]+1;i<=pos[r]-1;i++) tag[i]+=val;  //  mid
}

int cal(int l,int r,int val){
    int ans=-1;

    for(int i=l;i<=min(r,pos[l]*m);i++)
        if(tag[pos[l]]+s[i]<val) ans=max(ans,s[i]+tag[pos[l]]);//不断更新前驱的最大值

    if(pos[l]!=pos[r]){
        for(int i=(pos[r]-1)*m+1;i<=r;i++)
            if(tag[pos[r]]+s[i]<val) ans=max(ans,s[i]+tag[pos[r]]);
    }

    for(int i=pos[l]+1;i<=pos[r]-1;i++){
        auto it=st[i].lower_bound(val-tag[i]);
        if(it==st[i].begin()) continue;
        it--;
        ans=max(ans,*it+tag[i]);
    }
    return ans;
}


void solved(){
    read(n);m=sqrt(n);//块的大小;
    for(int i=1;i<=n;i++) read(s[i]);
    for(int i=1;i<=n;i++) {
        pos[i]=(i-1)/m+1;
        st[pos[i]].insert(s[i]);
    }
    for(int i=1;i<=n;i++){
        int ch,l,r,val;
        read(ch),read(l),read(r),read(val);
        if(ch==0) change(l,r,val);
        else writeln(cal(l,r,val));
    }
}

int main(){
    solved();
    return 0;
}

/**
数列分块4
链接:https://loj.ac/problem/6280
区间加法 + 区间求和
维护一个块的和:tmp[i]:第i块内元素的总和

对于不完整的块 直接暴力相加即可
对于完整的块 +tmp[i]即可;

过程注意细节...
*/

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

/**********************************************Head-----Template****************************************/
bool Finish_read;
template<class T>inline void read(T &x){Finish_read=0;x=0;int f=1;char ch=getchar();while(!isdigit(ch)){if(ch=='-')f=-1;if(ch==EOF)return;ch=getchar();}while(isdigit(ch))x=x*10+ch-'0',ch=getchar();x*=f;Finish_read=1;}
template<class T>inline void print(T x){if(x/10!=0)print(x/10);putchar(x%10+'0');}
template<class T>inline void writeln(T x){if(x<0)putchar('-');x=abs(x);print(x);putchar('\n');}
template<class T>inline void write(T x){if(x<0)putchar('-');x=abs(x);print(x);}
ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);}
ll lcm(ll a,ll b){ll gg=gcd(a,b);a/=gg;if(a<=LLONG_MAX/b) return a*b;return LLONG_MAX;}
/********************************Head----Temlate**********************************************/

const int maxn=1e5+7;
int n,m;
int s[maxn],tag[maxn],pos[maxn];
ll tmp[maxn];//当前块的和

vector<int>v[maxn];//有序的块 块内元素s[i];

void change(int l,int r,int val){
    for(int i=l;i<=min(r,pos[l]*m);i++) {
		s[i]+=val;
		tmp[pos[l]]+=val;
	}
    if(pos[l]!=pos[r]){
        for(int i=(pos[r]-1)*m+1;i<=r;i++){
			s[i]+=val;
			tmp[pos[r]]+=val;
        }
	}
    for(int i=pos[l]+1;i<=pos[r]-1;i++) {
		tag[i]+=val;
		tmp[i]+=val*m;
    }
}

int cal(int l,int r,int val,int mod){
    ll ans=0;
    for(int i=l;i<=min(r,pos[l]*m);i++)
        ans+=s[i]+tag[pos[l]],ans%=mod;
    if(pos[l]!=pos[r]){
        for(int i=(pos[r]-1)*m+1;i<=r;i++)
            ans+=s[i]+tag[pos[r]],ans%=mod;
    }

    for(int i=pos[l]+1;i<=pos[r]-1;i++) ans+=tmp[i],ans%=mod;
    return ans;
}


void solved(){
	memset(tmp,0,sizeof(tmp));
    read(n);m=sqrt(n);//块的大小;
    for(int i=1;i<=n;i++) read(s[i]);
    for(int i=1;i<=n;i++) {
        pos[i]=(i-1)/m+1;
        tmp[pos[i]]+=s[i];
        v[pos[i]].push_back(s[i]);
    }
    for(int i=1;i<=n;i++){
        int ch,l,r,val;
        read(ch),read(l),read(r),read(val);
        if(ch==0) change(l,r,val);
        else writeln(cal(l,r,val,val+1));
    }
}

int main(){
    solved();
    return 0;
}


/**
数列分块5
链接:https://loj.ac/problem/6281

区间开方+区间求和

对于整块进行开方时,必须要知道每一个元素,才能知道他们开方后的和,
也就是说,难以快速对一个块信息进行更新。
看来我们要另辟蹊径。不难发现,这题的修改就只有下取整开方,而一个数经过几次开方之后,它的值就会变成1。

如果每次区间开方只不涉及完整的块,意味着不超过2*sqrt(n)个元素,直接暴力即可。

如果涉及了一些完整的块,这些块经过几次操作以后就会都变成1,于是我们采取一种分块优化的暴力做法;
只要每个整块暴力开方后,记录一下元素是否都变成了1,区间修改时跳过那些全为 1 的块即可。

这样每个元素至多被开方不超过4次,显然复杂度没有问题。
*/

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

/**********************************************Head-----Template****************************************/
bool Finish_read;
template<class T>inline void read(T &x){Finish_read=0;x=0;int f=1;char ch=getchar();while(!isdigit(ch)){if(ch=='-')f=-1;if(ch==EOF)return;ch=getchar();}while(isdigit(ch))x=x*10+ch-'0',ch=getchar();x*=f;Finish_read=1;}
template<class T>inline void print(T x){if(x/10!=0)print(x/10);putchar(x%10+'0');}
template<class T>inline void writeln(T x){if(x<0)putchar('-');x=abs(x);print(x);putchar('\n');}
template<class T>inline void write(T x){if(x<0)putchar('-');x=abs(x);print(x);}
ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);}
ll lcm(ll a,ll b){ll gg=gcd(a,b);a/=gg;if(a<=LLONG_MAX/b) return a*b;return LLONG_MAX;}
/********************************Head----Temlate**********************************************/

const int maxn=1e5+7;
int n,m;
int s[maxn],tag[maxn],pos[maxn];
ll tmp[maxn];//当前块的和

vector<int>v[maxn];//   有序的块 块内元素s[i];
bool vis[maxn];   //    判断某块元素是否 all ===1;

void change(int l,int r){
    for(int i=l;i<=min(r,pos[l]*m);i++) {
		tmp[pos[l]]-=s[i];
		s[i]=sqrt(s[i]);
		tmp[pos[l]]+=s[i];
    }
    if(!vis[pos[l]]){// 左残块更新
		bool flag=1;
		for(int i=(pos[l]-1)*m+1;i<=pos[l]*m;i++) if(s[i]!=1) flag=0;
		vis[pos[l]]=flag;
    }
    if(pos[l]!=pos[r]){
        for(int i=(pos[r]-1)*m+1;i<=r;i++) {
			tmp[pos[r]]-=s[i];
			s[i]=sqrt(s[i]);
			tmp[pos[r]]+=s[i];
        }
	}
	if(!vis[pos[r]]){//右残块更新;
		bool flag=1;
		for(int i=(pos[r]-1)*m+1;i<=min(n,pos[r]*m);i++) if(s[i]!=1) flag=0;
		vis[pos[r]]=flag;
    }
    for(int i=pos[l]+1;i<=pos[r]-1;i++) {
		if(!vis[i]) {
			bool flag=1;
			for(int j=(i-1)*m+1;j<=min(n,i*m);j++) {
				tmp[i]-=s[j];
				s[j]=sqrt(s[j]);
			    tmp[i]+=s[j];
			    if(s[j]!=1) flag=0;
		    }
		    vis[i]=flag;
		}
    }
}

ll query(int l,int r){
    ll ans=0;
    for(int i=l;i<=min(r,pos[l]*m);i++) ans+=s[i];
    if(pos[l]!=pos[r]){
        for(int i=(pos[r]-1)*m+1;i<=r;i++) ans+=s[i];
    }
    for(int i=pos[l]+1;i<=pos[r]-1;i++) ans+=tmp[i];
    return ans;
}


void solved(){
	memset(tmp,0,sizeof(tmp));
	memset(vis,0,sizeof(vis));
    read(n);m=sqrt(n);
    for(int i=1;i<=n;i++) read(s[i]);
    for(int i=1;i<=n;i++) {
		pos[i]=(i-1)/m+1;
		tmp[pos[i]]+=s[i];
    }
    for(int i=1;i<=n;i++){
        int ch,l,r,val;
        read(ch),read(l),read(r),read(val);
        if(ch==0) change(l,r);//区间开方
        else writeln(query(l,r));//区间求和
    }
}

int main(){
    solved();
    return 0;
}

/**
数列分块 6
链接:https://loj.ac/problem/6282
单点插入+单点询问;

首先需要进行动态更新;
随机情况:
此题每块内可以放一个动态的数组;
每次插入时先找到位置所在的块,
再暴力插入,把块内的其它元素直接向后移动一位;
当然用链表也是可以的。

不随机:如果在询问之前在一个块内存在大量的插入
那么询问的时间复杂度就不能得到保证
因此每个块内部元素数量达到一定值时,需要进行重新分块;重构复杂度:o(n) 重构次数:blo;
这样的话时间复杂度就有了保证;

*/
#include<bits/stdc++.h>
#define ll long long
using namespace std;

/**********************************************Head-----Template****************************************/
bool Finish_read;
template<class T>inline void read(T &x){Finish_read=0;x=0;int f=1;char ch=getchar();while(!isdigit(ch)){if(ch=='-')f=-1;if(ch==EOF)return;ch=getchar();}while(isdigit(ch))x=x*10+ch-'0',ch=getchar();x*=f;Finish_read=1;}
template<class T>inline void print(T x){if(x/10!=0)print(x/10);putchar(x%10+'0');}
template<class T>inline void writeln(T x){if(x<0)putchar('-');x=abs(x);print(x);putchar('\n');}
template<class T>inline void write(T x){if(x<0)putchar('-');x=abs(x);print(x);}
ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);}
ll lcm(ll a,ll b){ll gg=gcd(a,b);a/=gg;if(a<=LLONG_MAX/b) return a*b;return LLONG_MAX;}
/********************************Head----Temlate**********************************************/

const int maxn=2e5+7;
int n,m,blo;
int st[maxn*2],tag[maxn],pos[maxn],s[maxn],num;

vector<int>v[maxn];//有序的块 块内元素s[i];

void reset(){//重新进行分块 o(n);
    int top=0;
    for(int i=1;i<=num;i++){
        for(auto it=v[i].begin();it!=v[i].end();it++) st[++top]=*it;
        v[i].clear();
    }
    int block=sqrt(top);
    for(int i=1;i<=top;i++) v[(i-1)/block+1].push_back(st[i]);
    num=(top-1)/block+1;
}

void _insert(int l,int r){
    int x=1;
    while(l>v[x].size()) l-=v[x++].size();
    v[x].insert(v[x].begin()+l-1,r);
    if(v[x].size()>blo*20) reset();//对于块内元素达到上限后 元素个数太多不能够保证询问的时间复杂度 因此需要进行重构;
}

void solved(){
    read(n);
    blo=sqrt(n);//块的大小;
    num=(n-1)/blo+1;//块的数量
    for(int i=1;i<=n;i++) read(s[i]);
    for(int i=1;i<=n;i++) v[(i-1)/blo+1].push_back(s[i]);
    for(int i=1;i<=n;i++){
        int ch,l,r,val;
        read(ch),read(l),read(r),read(val);
        if(ch==0) _insert(l,r);
        else {
            int x=1;
            while(r>v[x].size()) r-=v[x++].size();
            writeln(v[x][r-1]);
        }
    }
}

int main(){
    solved();
    return 0;
}

/**
数列分块入门7

链接:https://loj.ac/problem/6283
区间加法+区间乘法+单点查询;

单独加法和分块一没有很大区别,由于涉及到加法和乘法的联合使用,因此需要考虑优先级的问题;
若当前的一个块乘以m1后加上a1,这时进行一个乘m2的操作,则原来的标记变成m1*m2,a1*m2;
若当前的一个块乘以m1后加上a1,这时进行一个加a2的操作,则原来的标记变成m1,a1+a2;

因此需要维护两个块  一部分为乘块tagc[i] 另一部分为加块taga[i] 那么当前块内元素值为s[i]*tagc[pos[i]]+taga[pos[i]]; 仔细思考一下;
此题就是分清当前操作是+ / * 对应上述两部分taga[i] tagc[i]的更新;

对于不完整的块 应该预处理出的当前完整块所有的数s[i],涉及到+/*时 再进行上述的更新;

*/

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

/**********************************************Head-----Template****************************************/
bool Finish_read;
template<class T>inline void read(T &x){Finish_read=0;x=0;int f=1;char ch=getchar();while(!isdigit(ch)){if(ch=='-')f=-1;if(ch==EOF)return;ch=getchar();}while(isdigit(ch))x=x*10+ch-'0',ch=getchar();x*=f;Finish_read=1;}
template<class T>inline void print(T x){if(x/10!=0)print(x/10);putchar(x%10+'0');}
template<class T>inline void writeln(T x){if(x<0)putchar('-');x=abs(x);print(x);putchar('\n');}
template<class T>inline void write(T x){if(x<0)putchar('-');x=abs(x);print(x);}
ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);}
ll lcm(ll a,ll b){ll gg=gcd(a,b);a/=gg;if(a<=LLONG_MAX/b) return a*b;return LLONG_MAX;}
/********************************Head----Temlate**********************************************/

const int maxn=2e5+7;
const int mod=1e4+7;
int n,m,blo;
ll tagc[maxn],taga[maxn],tmp[maxn];

//表示cx + a

int pos[maxn],s[maxn],num;

vector<int>v[maxn];//有序的块 块内元素s[i];

void reset(int x){//将当前残缺块所在完整块进行更新(直接将以前操作完的数字直接放入s[i])
	for(int i=(x-1)*m+1;i<=min(x*m,n);i++) s[i]=(s[i]*tagc[x]+taga[x])%mod;
	taga[x]=0,tagc[x]=1;
}

void solve(int ch,int l,int r,int val) {
	reset(pos[l]);
	for(int i=l;i<=min(r,pos[l]*m);i++) {
		if(ch==0) s[i]+=val;
		else s[i]*=val;
		s[i]%=mod;
	}
	if(pos[l]!=pos[r]){
		reset(pos[r]);
		for(int i=(pos[r]-1)*m+1;i<=r;i++){
			if(ch==0) s[i]+=val;
			else s[i]*=val;
			s[i]%=mod;
		}
	}
	for(int i=pos[l]+1;i<=pos[r]-1;i++){
		if(ch==0) taga[i]+=val;
		else {
			taga[i]*=val;
			tagc[i]*=val;
		}
		taga[i]%=mod,tagc[i]%=mod;
	}
}

int query(int r){ return ( s[r]*tagc[pos[r]]+taga[pos[r]])%mod; }

void solved(){
    read(n);
    m=sqrt(n);//块的大小;

    for(int i=1;i<=n;i++) {
		read(s[i]);
	    while(s[i]<0) s[i]+=mod;s[i]%=mod;
    }
    for(int i=1;i<=n;i++) {
		pos[i]=(i-1)/m+1;
	    tmp[pos[i]]+=s[i],tmp[pos[i]]%=mod;
	    taga[pos[i]]=0,tagc[pos[i]]=1;
    }
    for(int i=1;i<=n;i++){
        int ch,l,r,val;
        read(ch),read(l),read(r),read(val);
        if(ch==2) writeln(query(r));
        else solve(ch,l,r,val);
    }
}

int main(){
    solved();
    return 0;
}

/**
数列分块8
链接:https://loj.ac/problem/6284
区间推平(修改) + 区间计数(等于val值的个数);

区间修改:直接维护一个tag数组 维护每块是否只有一种权值;

块数字相同即存在相同的权值 每块数字存在不同则为-1;
对于同权值的一个块o(1)记录ans 否则暴力记录答案 并修改s[i];

复杂度分析:
其实如果来说  看着感觉每次像是o(n)的复杂度 反过来说:
如果进行了o(n)的操作 那么相同元素的块也就是sqrt(n)了;
也就是说 只可能存在 进行一次o(n)

*/

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

/**********************************************Head-----Template****************************************/
bool Finish_read;
template<class T>inline void read(T &x){Finish_read=0;x=0;int f=1;char ch=getchar();while(!isdigit(ch)){if(ch=='-')f=-1;if(ch==EOF)return;ch=getchar();}while(isdigit(ch))x=x*10+ch-'0',ch=getchar();x*=f;Finish_read=1;}
template<class T>inline void print(T x){if(x/10!=0)print(x/10);putchar(x%10+'0');}
template<class T>inline void writeln(T x){if(x<0)putchar('-');x=abs(x);print(x);putchar('\n');}
template<class T>inline void write(T x){if(x<0)putchar('-');x=abs(x);print(x);}
ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);}
ll lcm(ll a,ll b){ll gg=gcd(a,b);a/=gg;if(a<=LLONG_MAX/b) return a*b;return LLONG_MAX;}
/********************************Head----Temlate**********************************************/

const int maxn=2e5+7;
int n,m;
int tag[maxn];
int pos[maxn],s[maxn];

void reset(int x){
	if(tag[x]==-1) return ;//当前块不一样直接退出 不需要将s[i] 更新
	for(int i=(x-1)*m+1;i<=min(n,x*m);i++) s[i]=tag[x];//由于当前区间被破坏了  因此 需要将tag值逐一赋值给s[i]也称还原;
	tag[x]=-1;//当然  区间修改 tag值可能被破坏之后 先默认tag值为-1了,后面也存在还原的操作;
}

void check(int x,int val){
	bool flag=1;
	for(int i=(x-1)*m+1;i<=min(n,x*m);i++) if(s[i]!=val) { flag=0;break; }
	if(flag) tag[x]=val;//检查被破坏之后  当前块是否一样; 还原tag
}

int solve(int l,int r,int val) {
	int ans=0;
	reset(pos[l]);
	for(int i=l;i<=min(r,pos[l]*m);i++) {
		if(s[i]==val) ans++;
		else  s[i]=val;
	}
	check(pos[l],val);
	if(pos[l]!=pos[r]){
		reset(pos[r]);
		for(int i=(pos[r]-1)*m+1;i<=r;i++) {
			if(s[i]==val) ans++;
			else s[i]=val;
		}
	}
	check(pos[r],val);
	for(int i=pos[l]+1;i<=pos[r]-1;i++){
		if(tag[i]!=-1){
			if(tag[i]==val) {
				if(pos[i]*m<=n) ans+=m;
				else ans+=(n-(pos[i]-1)*m);
			}
			else tag[i]=val;
		}
		else {
			for(int j=(i-1)*m+1;j<=min(i*m,n);j++)  if(s[j]==val) ans++;//由于是区间推平 那么当前块必然是相同的  也就不需要else s[i]=vla;
			tag[i]=val;
		}
	}
	return ans;
}

void solved(){
    read(n);
    m=sqrt(n);//块的大小;
    for(int i=1;i<=n;i++) read(s[i]);
    for(int i=1;i<=n;i++) pos[i]=(i-1)/m+1,tag[pos[i]]=-1;
    for(int i=1;i<=n;i++){
        int l,r,val;
        read(l),read(r),read(val);
        writeln(solve(l,r,val));
    }
}

int main(){
    solved();
    return 0;
}

/**
数列分块9
链接:https://loj.ac/problem/6285
查询区间最小众数;

首先离散化一下比较方便。
最小众数:完整的所有块的众数,和不完整块中出现的数。

所以我们可以预处理dp(i,j)表示第 i 块到第 j 块的众数 n*sqrt(n)
那么只要能快速得出一个数在某个区间内出现次数即可,每次只要比较至多2√n+1个元素的出现次数,这题就解决了。
由于没有修改,只要离散化以后,给每个数 x 开个vector;
按顺序存下x出现的位置;
每次询问 x 时把区间的左右端点放进对应 vector 二分一下即可。
具体细节  看注释........2018 7/27 
*/

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

/**********************************************Head-----Template****************************************/
bool Finish_read;
template<class T>inline void read(T &x){Finish_read=0;x=0;int f=1;char ch=getchar();while(!isdigit(ch)){if(ch=='-')f=-1;if(ch==EOF)return;ch=getchar();}while(isdigit(ch))x=x*10+ch-'0',ch=getchar();x*=f;Finish_read=1;}
template<class T>inline void print(T x){if(x/10!=0)print(x/10);putchar(x%10+'0');}
template<class T>inline void writeln(T x){if(x<0)putchar('-');x=abs(x);print(x);putchar('\n');}
template<class T>inline void write(T x){if(x<0)putchar('-');x=abs(x);print(x);}
ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);}
ll lcm(ll a,ll b){ll gg=gcd(a,b);a/=gg;if(a<=LLONG_MAX/b) return a*b;return LLONG_MAX;}
/********************************Head----Temlate**********************************************/

const int maxn=1e5+7;
vector<int>vec[maxn];
map<int,int>mp;

int id,n,m;
int dp[1000][1000];
int cnt[maxn],s[maxn];
int val[maxn],pos[maxn];//记录最原始的位置

void pre(int x){//直接暴力预处理出第x块  到x-->pos[n]块的最小众数
    memset(cnt,0,sizeof(cnt));
    int mx=0,ans=0;
    for(int i=(x-1)*m+1;i<=n;i++){
        cnt[s[i]]++;
        int tmp=pos[i];
        if(cnt[s[i]]>mx||(cnt[s[i]]==mx&&val[s[i]]<val[ans])) ans=s[i],mx=cnt[s[i]];
        dp[x][tmp]=ans;//对于当前块到下面接下来的块的最小众数;
    }
}

int query(int l,int r,int x){ return upper_bound(vec[x].begin(),vec[x].end(),r)-lower_bound(vec[x].begin(),vec[x].end(),l); }

int query(int l,int r){
    int ans,mx;
    ans=dp[pos[l]+1][pos[r]-1];//完整块的查询 为完整块的最小众数;
    mx=query(l,r,ans);//完整块众数出现的次数
    //非完整块的查询;
    for(int i=l;i<=min(r,pos[l]*m);i++){
        int t=query(l,r,s[i]);//暴力枚举非完整块的每一个数 对l r 进行查询 s[i]的个数 并且不断更新
        if(t>mx||(t==mx&&val[s[i]]<val[ans])) ans=s[i],mx=t;
    }
    if(pos[l]!=pos[r]){
        for(int i=(pos[r]-1)*m+1;i<=r;i++){
            int t=query(l,r,s[i]);
            if(t>mx||(t==mx&&val[s[i]]<val[ans])) ans=s[i],mx=t;
        }
    }
    return ans;
}

void solved(){
    read(n);
    m=sqrt(n);//块的大小;
    for(int i=1;i<=n;i++) {
        read(s[i]);
        if(!mp[s[i]]){
            mp[s[i]]=++id; //离散化
            val[id]=s[i];  //val存储离散化之前的值
        }
        s[i]=mp[s[i]];
        vec[s[i]].push_back(i);
        /**
        算是反转思想吧
        将s[i]出现过的角标存储到对应的vector
        eg: 对于l r val 出现的次数 可以直接在  vec[val] 内进行二分查找 很奇妙的一个想法;
        想法类似于HDU 口算训练
        */
    }
    for(int i=1;i<=n;i++) pos[i]=(i-1)/m+1;
    for(int i=1;i<=pos[n];i++) pre(i);//预处理出第i块到后面块的最小众数;
    for(int i=1;i<=n;i++){
        int l,r;scanf("%d %d",&l,&r);
        if(l>r) swap(l,r);
        writeln(val[query(l,r)]);
    }
}

int main(){
    solved();
    return 0;
}

猜你喜欢

转载自blog.csdn.net/hypHuangYanPing/article/details/81208367
今日推荐