/**
数列分块: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;
}