ACM模板
放暑假了,回归!!!
自己不会写暴力,而且好久没写代码了,于是学学分块的优雅暴力~
「分块入门-LibreOJ」
「分块」数列分块入门1 – 9 by hzwer
数列简单分块问题实际上有三项东西要我们思考:
对于每次区间操作:
-
不完整的块 的O(n)O(\sqrt n)O(n)个元素怎么处理?
-
O(n)O(\sqrt n)O(n)个 整块 怎么处理?
-
要预处理什么信息(复杂度不能超过后面的操作一般O(NN)O(N\sqrt N)O(NN)预处理)
下面假设块的大小为MMM
#6277. 数列分块入门 1
小块暴力,大块懒标记,只需要思考如何快速查询块内的答案即可
#include<bits/stdc++.h>using namespace std;constexpr int N=500010;int a[N],n;
int b[N];
int sz;
void modify(int l,int r,int c)
{int idl=l/sz,idr=r/sz;if(idl==idr){for(int i=l;i<=r;i++) a[i]+=c;return;}for(int i=l;i<(idl+1)*sz;i++) a[i]+=c;for(int i=idl+1;i<idr;i++) b[i]+=c;for(int i=idr*sz;i<=r;i++) a[i]+=c;
}
int query(int r){return a[r]+b[r/sz];}
int main()
{ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);cin>>n;sz=sqrt(n);for(int i=1;i<=n;i++) cin>>a[i];for(int i=1;i<=n;i++){int op,l,r,c;cin>>op>>l>>r>>c;if(op==0)modify(l,r,c);elsecout<<query(r)<<'\n';}return 0;
}
#6278. 数列分块入门 2
不完整的块只需要暴力扫一遍,单词询问复杂度O(M)O(M)O(M)
完整的块内维护有序数列,每次要二分询问,单词询问复杂度为O(NMlogM)O(\frac N M \log M)O(MNlogM)
询问复杂度O{K(M+NMlogM)}O\{K(M+\frac{N}{M}\log M)\}O{K(M+MNlogM)}’
显然区间假发对于完整的快来说不改变块内的顺序,而不完整的块每次只需要重新排序暴力维护一边即可,每次只可能有两个不完整的块,因此单词修改时间复杂度O(MlogM)O(M\log M)O(MlogM)
修改时间复杂度O{K(MlogM+NM)}O\{K(M\log M+\frac{N}{M})\}O{K(MlogM+MN)}
#include<bits/stdc++.h>using namespace std;constexpr int N=50010;int n,a[N],b[N];
int sz;
vector<int> vec[810];
int s[N];
void reset(int o)
{vec[o].clear();for(int i=(o-1)*sz+1;i<=min(o*sz,n);i++) vec[o].push_back(a[i]);sort(vec[o].begin(),vec[o].end());
}
void update(int l,int r,int c)
{if(b[l]==b[r]){for(int i=l;i<=r;i++) a[i]+=c;reset(b[l]);//重排序return;}for(int i=l;i<=sz*b[l];i++) a[i]+=c; reset(b[l]);for(int i=(b[r]-1)*sz+1;i<=r;i++) a[i]+=c; reset(b[r]);for(int i=b[l]+1;i<b[r];i++) s[i]+=c; //完整的块直接加
}
int query(int l,int r,int c)
{int ans=0;if(b[l]==b[r]){for(int i=l;i<=r;i++) ans+=(a[i]+s[b[i]]<c?1:0);return ans;}for(int i=l;i<=sz*b[l];i++) ans+=(a[i]+s[b[i]]<c?1:0);for(int i=(b[r]-1)*sz+1;i<=r;i++) ans+=(a[i]+s[b[i]]<c?1:0);for(int i=b[l]+1;i<b[r];i++)ans+=lower_bound(vec[i].begin(),vec[i].end(),c-s[i])-vec[i].begin();return ans;
}
int main()
{ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);cin>>n;for(int i=1;i<=n;i++) cin>>a[i];sz=sqrt(n);for(int i=1;i<=n;i++) b[i]=(i-1)/sz+1, vec[b[i]].push_back(a[i]);for(int i=1;i<=b[n];i++) sort(vec[i].begin(),vec[i].end());for(int i=1;i<=n;i++){int op,l,r,c;cin>>op>>l>>r>>c;if(op==0)update(l,r,c);elsecout<<query(l,r,c*c)<<'\n';}return 0;
}
#6279. 数列分块入门 3
接着第二题的解法,其实只要把块内查询的二分稍作修改即可。
不过这题其实想表达:可以在块内维护其它结构使其更具有拓展性,比如放一个 set ,这样如果还有插入、删除元素的操作,会更加的方便。
#include<bits/stdc++.h>using namespace std;constexpr int N=100010;int n,a[N],b[N];
int sz;
vector<int> vec[1010];
int s[N];
void reset(int o)
{vec[o].clear();for(int i=(o-1)*sz+1;i<=min(o*sz,n);i++) vec[o].push_back(a[i]);sort(vec[o].begin(),vec[o].end());
}
void update(int l,int r,int c)
{if(b[l]==b[r]){for(int i=l;i<=r;i++) a[i]+=c;reset(b[l]);return;}for(int i=l;i<=sz*b[l];i++) a[i]+=c; reset(b[l]);for(int i=(b[r]-1)*sz+1;i<=r;i++) a[i]+=c; reset(b[r]);for(int i=b[l]+1;i<b[r];i++) s[i]+=c;
}
int query(int l,int r,int c)
{int ans=-1;if(b[l]==b[r]){for(int i=l;i<=r;i++) if(a[i]+s[b[i]]<c) ans=max(ans,a[i]+s[b[i]]);return ans;}for(int i=l;i<=sz*b[l];i++) if(a[i]+s[b[i]]<c) ans=max(ans,a[i]+s[b[i]]);for(int i=(b[r]-1)*sz+1;i<=r;i++) if(a[i]+s[b[i]]<c) ans=max(ans,a[i]+s[b[i]]);for(int i=b[l]+1;i<b[r];i++){auto t=lower_bound(vec[i].begin(),vec[i].end(),c-s[i]);if(t!=vec[i].begin()) --t;if(*t<c-s[i]) ans=max(ans,*t+s[i]);}return ans;
}
int main()
{ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);cin>>n;for(int i=1;i<=n;i++) cin>>a[i];sz=sqrt(n);for(int i=1;i<=n;i++) b[i]=(i-1)/sz+1, vec[b[i]].push_back(a[i]);for(int i=1;i<=b[n];i++) sort(vec[i].begin(),vec[i].end());for(int i=1;i<=n;i++){int op,l,r,c;cin>>op>>l>>r>>c;if(op==0)update(l,r,c);elsecout<<query(l,r,c)<<'\n';}return 0;
}
multiset
#include<bits/stdc++.h>using namespace std;constexpr int N=100010;int n,a[N],b[N];
int sz;
multiset<int> st[1010];
int s[N];void update(int l,int r,int c)
{if(b[l]==b[r]){for(int i=l;i<=r;i++) st[b[l]].erase(st[b[l]].find(a[i])),st[b[l]].insert(a[i]+c),a[i]+=c;return;}for(int i=l;i<=sz*b[l];i++) st[b[l]].erase(st[b[l]].find(a[i])),st[b[l]].insert(a[i]+c),a[i]+=c;for(int i=(b[r]-1)*sz+1;i<=r;i++) st[b[r]].erase(st[b[r]].find(a[i])),st[b[r]].insert(a[i]+c),a[i]+=c;for(int i=b[l]+1;i<b[r];i++) s[i]+=c;
}
int query(int l,int r,int c)
{int ans=-1;if(b[l]==b[r]){for(int i=l;i<=r;i++) if(a[i]+s[b[i]]<c) ans=max(ans,a[i]+s[b[i]]);return ans;}for(int i=l;i<=sz*b[l];i++) if(a[i]+s[b[i]]<c) ans=max(ans,a[i]+s[b[i]]);for(int i=(b[r]-1)*sz+1;i<=r;i++) if(a[i]+s[b[i]]<c) ans=max(ans,a[i]+s[b[i]]);for(int i=b[l]+1;i<b[r];i++){auto t=st[i].lower_bound(c-s[i]);if(t!=st[i].begin()) --t;if(*t<c-s[i]) ans=max(ans,*t+s[i]);}return ans;
}
int main()
{ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);cin>>n;for(int i=1;i<=n;i++) cin>>a[i];sz=sqrt(n);for(int i=1;i<=n;i++) b[i]=(i-1)/sz+1, st[b[i]].insert(a[i]);for(int i=1;i<=n;i++){int op,l,r,c;cin>>op>>l>>r>>c;if(op==0)update(l,r,c);elsecout<<query(l,r,c)<<'\n';}return 0;
}
#6280. 数列分块入门 4
询问变成了区间上的询问,不完整的块还是暴力;而要想快速统计完整块的答案,需要维护每个块的元素和,先要预处理一下。
考虑区间修改操作,不完整的块直接改,顺便更新块的元素和;完整的块类似之前标记的做法,直接根据块的元素和所加的值计算元素和的增量。
#include<bits/stdc++.h>using namespace std;constexpr int N=100010;int n,a[N],b[N];
int sz;
long long s[N],tag[N];
void update(int l,int r,int c)
{if(b[l]==b[r]){for(int i=l;i<=r;i++) a[i]+=c,s[b[i]]+=c;return;}for(int i=l;i<=sz*b[l];i++) a[i]+=c,s[b[i]]+=c;for(int i=(b[r]-1)*sz+1;i<=r;i++) a[i]+=c,s[b[i]]+=c;for(int i=b[l]+1;i<b[r];i++) s[i]+=1ll*c*sz,tag[i]+=c;
}
long long query(int l,int r,int mod)
{long long ans=0;if(b[l]==b[r]){for(int i=l;i<=r;i++) ans+=a[i]+tag[b[i]],ans%=mod;return ans;}for(int i=l;i<=sz*b[l];i++) ans+=a[i]+tag[b[i]],ans%=mod;for(int i=(b[r]-1)*sz+1;i<=r;i++) ans+=a[i]+tag[b[i]],ans%=mod;for(int i=b[l]+1;i<b[r];i++) ans+=s[i],ans%=mod;return ans;
}
int main()
{ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);cin>>n;for(int i=1;i<=n;i++) cin>>a[i];sz=sqrt(n);for(int i=1;i<=n;i++) b[i]=(i-1)/sz+1,s[b[i]]+=a[i];for(int i=1;i<=n;i++){int op,l,r,c;cin>>op>>l>>r>>c;if(op==0)update(l,r,c);elsecout<<query(l,r,c+1)<<'\n';}return 0;
}
#6281. 数列分块入门 5
对于一个数,开方多次后就会变成1
不完整的块直接暴力开方,而完整的块记录下当前块内的数是不是已经全为1了,如果全为1就不需要开发,如果不是暴力开方。
#include<bits/stdc++.h>using namespace std;constexpr int N=100010;int n,a[N],b[N];
int sz;
int s[N],fg[N];
void solve(int k)
{if(fg[k]) return; // 该块已经全为1了fg[k]=1;s[k]=0;for(int i=(k-1)*sz+1;i<=k*sz;i++) //暴力开方{a[i]=sqrt(a[i]);s[k]+=a[i];if(a[i]>1) fg[k]=0;}
}
void update(int l,int r)
{if(b[l]==b[r]) {for(int i=l;i<=r;i++) s[b[i]]-=a[i],a[i]=sqrt(a[i]),s[b[i]]+=a[i];return;}for(int i=l;i<=b[l]*sz;i++) s[b[i]]-=a[i],a[i]=sqrt(a[i]),s[b[i]]+=a[i];for(int i=(b[r]-1)*sz+1;i<=r;i++) s[b[i]]-=a[i],a[i]=sqrt(a[i]),s[b[i]]+=a[i];for(int i=b[l]+1;i<b[r];i++) solve(i);
}
int query(int l,int r)
{int ans=0;if(b[l]==b[r]){for(int i=l;i<=r;i++) ans+=a[i];return ans;}for(int i=l;i<=b[l]*sz;i++) ans+=a[i];for(int i=(b[r]-1)*sz+1;i<=r;i++) ans+=a[i];for(int i=b[l]+1;i<b[r];i++) ans+=s[i];return ans;
}
int main()
{ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);cin>>n;for(int i=1;i<=n;i++) cin>>a[i];sz=sqrt(n);for(int i=1;i<=n;i++) b[i]=(i-1)/sz+1,s[b[i]]+=a[i];for(int i=1;i<=n;i++){int op,l,r,c;cin>>op>>l>>r>>c;if(op==0)update(l,r);elsecout<<query(l,r)<<'\n';}return 0;
}
#6282. 数列分块入门 6
随机数据的情况
我们完整块内可以用数组以外的数据结构,能够支持其它不一样的操作,比如此题每块内可以放一个动态的数组vector,每次插入时先找到位置所在的块,再暴力插入,把块内的其它元素直接向后移动一位,当然用链表也是可以的。
如果先在一个块有大量单点插入,这个块的大小会大大超过√n,那块内的暴力就没有复杂度保证了。
每N\sqrt NN插入后,重新把数列平均分一下块,重构需要的复杂度为O(N),重构的次数为N\sqrt NN,所以重构的复杂度没有问题,而且保证了每个块的大小相对均衡。
当然,也可以当某个块过大时重构,或者只把这个块分成两半。
#include<bits/stdc++.h>using namespace std;constexpr int N=200010;
int a[N],b[N];
int n,m,sz;
vector<int> vec[1010];
int bkp[N];pair<int,int> query(int k)
{int now=1;while(k>vec[now].size()){k-=vec[now].size();now++;}return make_pair(now,k-1);//vector 下标从0开始
}
void rebuild()
{int tot=0;for(int i=1;i<=m;i++){for(auto t:vec[i]) bkp[++tot]=t;vec[i].clear();}int gap=sqrt(tot);for(int i=1;i<=tot;i++) b[i]=(i-1)/gap+1,vec[b[i]].push_back(bkp[i]);m=(tot-1)/gap+1;
}
void insert(int k,int x)
{auto t=query(k);vec[t.first].insert(vec[t.first].begin()+t.second,x);if(vec[t.first].size()>20*sz) rebuild();
}
int main()
{ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);cin>>n;for(int i=1;i<=n;i++) cin>>a[i];sz=sqrt(n);for(int i=1;i<=n;i++) b[i]=(i-1)/sz+1,vec[b[i]].push_back(a[i]);m=(n-1)/sz+1;for(int i=1;i<=n;i++){int op,l,r,c;cin>>op>>l>>r>>c;if(op==0)insert(l,r);else{auto t=query(r);cout<<vec[t.first][t.second]<<'\n';}}return 0;
}
#6283. 数列分块入门 7
类似线段树的区间+和区间×即可。
每次区间×的时候可以reset一下,去除以前的块标记。
#include<bits/stdc++.h>using namespace std;constexpr int N=200010;
constexpr int mod=10007;
int a[N],b[N];
int n,m,sz;
int tga[N],tgm[N];
int query(int k){return (a[k]*tgm[b[k]]%mod+tga[b[k]])%mod;}
void reset(int k)
{for(int i=(k-1)*sz+1;i<=min(n,sz*k);i++) a[i]=query(i);tga[k]=0,tgm[k]=1;
}
void add(int l,int r,int c)
{if(b[l]==b[r]){reset(b[l]);for(int i=l;i<=r;i++) a[i]+=c,a[i]%=mod;return;}reset(b[l]),reset(b[r]);for(int i=l;i<=b[l]*sz;i++) a[i]+=c,a[i]%=mod;for(int i=(b[r]-1)*sz+1;i<=r;i++) a[i]+=c,a[i]%=mod;for(int i=b[l]+1;i<b[r];i++) tga[i]+=c,tga[i]%=mod;
}
void mul(int l,int r,int c)
{c%=mod;if(b[l]==b[r]){reset(b[l]);for(int i=l;i<=r;i++) a[i]*=c,a[i]%=mod;return;}reset(b[l]),reset(b[r]);for(int i=l;i<=b[l]*sz;i++) a[i]*=c,a[i]%=mod;for(int i=(b[r]-1)*sz+1;i<=r;i++) a[i]*=c,a[i]%=mod;for(int i=b[l]+1;i<b[r];i++) tgm[i]*=c,tgm[i]%=mod,tga[i]*=c,tga[i]%=mod;
}
int main()
{ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);cin>>n;for(int i=1;i<=n;i++) cin>>a[i];sz=sqrt(n);for(int i=1;i<=n;i++) b[i]=(i-1)/sz+1;for(int i=1;i<=b[n];i++) tgm[i]=1;for(int i=1;i<=n;i++){int op,l,r,c;cin>>op>>l>>r>>c;if(op==0)add(l,r,c);else if(op==1)mul(l,r,c);elsecout<<query(r)<<'\n';}return 0;
}
#6284. 数列分块入门 8
区间修改没有什么难度,这题难在区间查询比较奇怪,因为权值种类比较多,似乎没有什么好的维护方法。
模拟一些数据可以发现,询问后一整段都会被修改,几次询问后数列可能只剩下几段不同的区间了。
我们思考这样一个暴力,还是分块,维护每个分块是否只有一种权值,区间操作的时候,对于同权值的一个块就O(1)统计答案,否则暴力统计答案,并修改标记,不完整的块也暴力。
这样看似最差情况每次都会耗费O(n)的时间,但其实可以这样分析:
假设初始序列都是同一个值,那么查询是O(√n),如果这时进行一个区间操作,它最多破坏首尾2个块的标记,所以只能使后面的询问至多多2个块的暴力时间,所以均摊每次操作复杂度还是O(√n)。
换句话说,要想让一个操作耗费O(n)的时间,要先花费√n个操作对数列进行修改。
初始序列不同值,经过类似分析后,就可以放心的暴力啦。
#include<bits/stdc++.h>using namespace std;constexpr int N=100010;
constexpr int mod=10007;
int a[N],b[N];
int n,m,sz;
int tg[N];
void reset(int k)
{if(tg[k]==-1) return;for(int i=(k-1)*sz+1;i<=min(n,k*sz);i++) a[i]=tg[k];tg[k]=-1;
}
int solve(int l,int r,int c)
{int ans=0;reset(b[l]);for(int i=l;i<=min(r,b[l]*sz);i++){if(a[i]==c) ans++;else a[i]=c;}if(b[l]!=b[r]){reset(b[r]);for(int i=(b[r]-1)*sz+1;i<=r;i++){if(a[i]==c) ans++;else a[i]=c;}}for(int i=b[l]+1;i<b[r];i++){if(tg[i]!=-1){if(tg[i]==c) ans+=sz;else tg[i]=c;}else{for(int j=(i-1)*sz+1;j<=i*sz;j++){if(a[j]==c) ans++;else a[j]=c;}tg[i]=c;}}return ans;
}
int main()
{ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);cin>>n;for(int i=1;i<=n;i++) cin>>a[i];sz=sqrt(n);memset(tg,-1,sizeof tg);for(int i=1;i<=n;i++) b[i]=(i-1)/sz+1;for(int i=1;i<=n;i++){int l,r,c;cin>>l>>r>>c;cout<<solve(l,r,c)<<'\n';}return 0;
}
#6285. 数列分块入门 9
由于aia_iai在[−231,231−1][-2^{31},2^{31}-1][−231,231−1],因此需要离散化
然后建立一个vector<int>vec[N]
存储每个数出现的位置,有了这个,我们就可以二分查找出一个数在区间l~r内出现的次数
区间众数一定是大块的众数或者是边界小块出现过的数
预处理大块之间的众数,小块暴力查询每个数在[l,r]出现的次数。
如果块的大小为N\sqrt{N}N,时间复杂度为O{NNlogN}O\{N \sqrt N \log N\}O{NNlogN}
#include<bits/stdc++.h>using namespace std;constexpr int N=100010;
constexpr int mod=10007;
int a[N],va[N],b[N];
int n,m,sz;
int idx;
int dp[1005][1005];
vector<int> vec[N];
int rd()
{int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;
}
void prework()
{// 离散化sort(va+1,va+1+m);m=unique(va+1,va+1+m)-va-1;for(int i=1;i<=n;i++) {a[i]=lower_bound(va+1,va+1+m,a[i])-va;vec[a[i]].push_back(i);}sz=sqrt(n);for(int i=1;i<=n;i++) b[i]=(i-1)/sz+1;// 预处理块众数for(int k=1;k<=b[n];k++){static int cnt[N];memset(cnt,0,sizeof cnt);int ans=0,mx=0;for(int i=(k-1)*sz+1;i<=n;i++){cnt[a[i]]++;if(cnt[a[i]]>mx||(cnt[a[i]]==mx&&va[a[i]]<va[ans])){ans=a[i];mx=cnt[a[i]];}dp[k][b[i]]=ans;}}
}
int solve(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=dp[b[l]+1][b[r]-1];int mx=solve(l,r,ans);for(int i=l;i<=min(r,sz*b[l]);i++){int tmp=solve(l,r,a[i]);if(tmp>mx||(tmp==mx&&va[a[i]]<va[ans])){mx=tmp;ans=a[i];}}if(b[l]!=b[r]){for(int i=(b[r]-1)*sz+1;i<=r;i++){int tmp=solve(l,r,a[i]);if(tmp>mx||(tmp==mx&&va[a[i]]<va[ans])){mx=tmp;ans=a[i];}}}return ans;
}
int main()
{n=rd();for(int i=1;i<=n;i++){a[i]=rd();va[++m]=a[i];}prework();for(int i=1;i<=n;i++){int l=rd(),r=rd();printf("%d\n",va[query(l,r)]);}return 0;
}
如何快速查询块之间一个数出现次数?
预处理s[i][j]前缀和 0~i块中j出现的次数
小块暴力枚举,大块查表
这样的时间复杂度是标准的O(NN)O(N\sqrt N)O(NN)
#include<bits/stdc++.h>using namespace std;constexpr int N=100010;
constexpr int mod=10007;
int a[N],va[N],b[N];
int n,m,sz;
int dp[1005][1005];
int s[1005][N];
int rd()
{int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;
}
void prework()
{// 离散化sort(va+1,va+1+m);m=unique(va+1,va+1+m)-va-1;for(int i=1;i<=n;i++) a[i]=lower_bound(va+1,va+1+m,a[i])-va;sz=sqrt(n);for(int i=1;i<=n;i++) b[i]=(i-1)/sz+1;// 预处理块众数for(int k=1;k<=b[n];k++){static int cnt[N];memset(cnt,0,sizeof cnt);int ans=0,mx=0;for(int i=(k-1)*sz+1;i<=n;i++){cnt[a[i]]++;if(cnt[a[i]]>mx||(cnt[a[i]]==mx&&va[a[i]]<va[ans])){ans=a[i];mx=cnt[a[i]];}dp[k][b[i]]=ans;}}//预处理前缀和块for(int i=1;i<=n;i++){s[b[i]][a[i]]++;if(b[i+1]!=b[i])// i是该块的最后一个for(int j=1;j<=n;j++) s[b[i]][j]+=s[b[i]-1][j];}
}
int solve(int l,int r,int x)
{if(l>r) return 0;return s[r][x]-s[l-1][x];
}
int query(int l,int r)
{int ans=dp[b[l]+1][b[r]-1];// 块间众数int mx=solve(b[l]+1,b[r]-1,ans);static int mp[N];memset(mp,0,sizeof mp);for(int i=l;i<=min(r,sz*b[l]);i++) mp[a[i]]++;if(b[l]!=b[r]) for(int i=(b[r]-1)*sz+1;i<=r;i++) mp[a[i]]++;for(int i=l;i<=min(r,sz*b[l]);i++)if((mp[a[i]]+solve(b[l]+1,b[r]-1,a[i])>mx)||(mp[a[i]]+solve(b[l]+1,b[r]-1,a[i])==mx&&va[a[i]]<va[ans])){mx=mp[a[i]]+solve(b[l]+1,b[r]-1,a[i]);ans=a[i];}if(b[l]!=b[r]) for(int i=(b[r]-1)*sz+1;i<=r;i++){if((mp[a[i]]+solve(b[l]+1,b[r]-1,a[i])>mx)||(mp[a[i]]+solve(b[l]+1,b[r]-1,a[i])==mx&&va[a[i]]<va[ans])){mx=mp[a[i]]+solve(b[l]+1,b[r]-1,a[i]);ans=a[i];}}return va[ans];
}
int main()
{n=rd();for(int i=1;i<=n;i++){a[i]=rd();va[++m]=a[i];}prework();for(int i=1;i<=n;i++){int l=rd(),r=rd();printf("%d\n",query(l,r));}return 0;
}
要加油哦~