「分块」数列分块入门1 – 9

ACM模板


放暑假了,回归!!!
自己不会写暴力,而且好久没写代码了,于是学学分块的优雅暴力~

「分块入门-LibreOJ」
「分块」数列分块入门1 – 9 by hzwer

数列简单分块问题实际上有三项东西要我们思考:

对于每次区间操作:

  1. 不完整的块 的O(n)O(\sqrt n)O(n)个元素怎么处理?

  2. O(n)O(\sqrt n)O(n)个 整块 怎么处理?

  3. 要预处理什么信息(复杂度不能超过后面的操作一般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(NMlog⁡M)O(\frac N M \log M)O(MNlogM)

询问复杂度O{K(M+NMlog⁡M)}O\{K(M+\frac{N}{M}\log M)\}O{K(M+MNlogM)}

显然区间假发对于完整的快来说不改变块内的顺序,而不完整的块每次只需要重新排序暴力维护一边即可,每次只可能有两个不完整的块,因此单词修改时间复杂度O(Mlog⁡M)O(M\log M)O(MlogM)

修改时间复杂度O{K(Mlog⁡M+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,2311],因此需要离散化

然后建立一个vector<int>vec[N]存储每个数出现的位置,有了这个,我们就可以二分查找出一个数在区间l~r内出现的次数
区间众数一定是大块的众数或者是边界小块出现过的数
预处理大块之间的众数,小块暴力查询每个数在[l,r]出现的次数。

如果块的大小为N\sqrt{N}N,时间复杂度为O{NNlog⁡N}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;
}

要加油哦~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/318915.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C#:在Task中使用依赖注入的Service/EFContext

dotnet core时代,依赖注入基本已经成为标配了,这就不多说了.前几天在做某个功能的时候遇到在Task中使用EF DbContext的问题,学艺不精的我被困扰了不短的一段时间,于是有了这个文章.先说一下代码结构和场景.首先有一个HouseDbContext,代码大概是下面这样:public class HouseDbCo…

牛客题霸 [ 最长公共前缀] C++题解/答案

牛客题霸 [ 最长公共前缀] C题解/答案 题目描述 编写一个函数来查找字符串数组中的最长公共前缀。 题解&#xff1a; 原本想暴力做&#xff0c;直接多层循环套&#xff0c;发现并不用 注意本题&#xff0c;给出了多组字符串&#xff0c;求它们的最长公共前缀&#xff0c;这…

pkusc2021游记

文章目录Day 0Day 1Day 2Day 3Day 0 车&#xff0c;公交&#xff0c;飞机&#xff0c;公交&#xff0c;车 坐了半天的交通终于到了&#xff0c;整个人都坐的晕乎乎的&#xff0c;然后看了下学校位置吃了饭就没事回酒店了。 Day 1 早上是报道&#xff0c;九点才开始&#xf…

codeforces82 D. Two out of Three(记忆化搜索)

D. Two out of Three 设fi,jf_{i,j}fi,j​表示为当前队伍开头的两个人是i,ji,ji,j时最小代价&#xff0c;分析可知本轮可以选择的方案有 i,ji,ji,j两人&#xff0c;fi,jaiajfj1,j2f_{i,j}a_ia_jf_{j1,j2}fi,j​ai​aj​fj1,j2​i,j1i,j1i,j1两人&#xff0c;fi,jaiaj1fj,j2f_{…

DevOps/.NET 微服务 秋季分享会领优惠门票

参与活动【活动&#xff08;深圳&#xff09;】DevOps/.NET 微服务 秋季分享会&#xff0c;优惠门票等你来领 的以下10位同学微信联系我: geffzhang 领票

bzoj3729-Gty的游戏【Splay,博弈论】

正题 题目链接:https://darkbzoj.tk/problem/3729 题目大意 给出nnn个点的一棵树&#xff0c;第iii个节点上有aia_iai​个石子&#xff0c;然后每次可以选择不超过LLL个石子移动到父节点处。要求支持操作 以一个节点的子树进行博弈是否有先手必胜修改一个节点的石子个数插入…

牛客题霸 [ 求路径] C++题解/答案

牛客题霸 [ 求路径] C题解/答案 题目描述 一个机器人在mn大小的地图的左上角&#xff08;起点&#xff0c;下图中的标记“start"的位置&#xff09;。 机器人每次向下或向右移动。机器人要到达地图的右下角。&#xff08;终点&#xff0c;下图中的标记“Finish"的位…

【.NET Core项目实战-统一认证平台】第七章 网关篇-自定义客户端限流

上篇文章我介绍了如何在网关上增加自定义客户端授权功能&#xff0c;从设计到编码实现&#xff0c;一步一步详细讲解&#xff0c;相信大家也掌握了自定义中间件的开发技巧了&#xff0c;本篇我们将介绍如何实现自定义客户端的限流功能&#xff0c;来进一步完善网关的基础功能。…

codeforces855 C. Helga Hufflepuff‘s Cup(树形dp)

C. Helga Hufflepuff’s Cup 比较经典的树形dp套路。由于题目限制某类点的数量&#xff0c;因而状态需要记录一下子树中该特殊点的数量。 设计dp&#xff1a; fu,j,kf_{u,j,k}fu,j,k​以uuu为根的子树中&#xff0c;选了jjj个特殊值&#xff08;点权K&#xff09;&#xff0c…

牛客题霸 [链表中倒数第k个结点] C++题解/答案

链表中倒数第k个结点 题解&#xff1a; 我们把链表存两份&#xff0c;让其中一个先走k个节点&#xff0c;然后两个链表一起走&#xff0c;直到第一个链表到头&#xff0c;因为两个相差k个节点&#xff0c;所以第二个链表停的位置就是倒数第k个 代码&#xff1a; /* struct …

P3343-[ZJOI2015]地震后的幻想乡【dp,数学期望】

正题 题目链接:https://www.luogu.com.cn/problem/P3343 题目大意 给出nnn个点的一张无向图&#xff0c;每条边被修复的时间是[0,1][0,1][0,1]的一个随机实数&#xff0c;求这张图联通期望时间。 1≤n≤10,m≤n(n−1)21\leq n\leq 10,m\leq \frac{n(n-1)}{2}1≤n≤10,m≤2n(n…

codeforces229 D. Towers(dp+贪心)

D. Towers 设计dp&#xff1a; fif_ifi​表示考虑到iii时满足题意的最小代价。 考虑最后一步&#xff0c;也就是最后一组合并即fifj{aj1⋯ai}f_if_j\{a_{j1}\dotsa_i\}fi​fj​{aj1​⋯ai​} 在满足最优条件的同时让当前aia_iai​最小&#xff08;贪心&#xff09; #include&…

1.14 日志(递推ybtoj)

明天要模拟&#xff0c;n年未碰电脑&#xff0c;先不学新的了。。。。 1.错排问题 dp最棒了 code: #include #include #include #include #include using namespace std; const int MINT_MAX; long long f[25][25]{ };//f[i][j]表示有i个数&#xff0c;其中j个数可以随便填 …

牛客题霸 [买卖股票的最好时机] C++题解/答案

买卖股票的最好时机 题解&#xff1a; 设两个变量&#xff0c;一个为minn&#xff0c;一个为maxx 当遇到比当前minn还小的价钱时&#xff0c;就更新minn 当与minn大时&#xff0c;就计算如果这个价卖出&#xff0c;赚多少钱&#xff0c;maxx不断更新取最大值 这样&#xff0c…

P7600-[APIO2021]封闭道路【堆,dp】

正题 题目链接:https://www.luogu.com.cn/problem/P7600 题目大意 给出nnn个点的一棵树&#xff0c;边有边权&#xff0c;对于每个kkk求去掉最小边权和的点使得每个点的度数都不超过kkk。 1≤n≤1051\leq n\leq 10^51≤n≤105 题目大意 APIO遇到的原题&#xff0c;和CF1119F…

codeforces1497 D. Geniue(dp+图论)

D. Geniue Frozen_Guardian题解 Implicit_总结 首先把此序列看作一个完全图&#xff0c;然后按照边权从小到大的顺序枚举边。 如何按照边权从小到大枚举边? 下面考虑形如边(a,b)(a,b)(a,b)都默认a<ba<ba<b。 任意考虑两条边(a,b)(a,b)(a,b)和(c,d)(c,d)(c,d)&…

1.16 模拟(排序)

# 总结 845分 我好菜啊qwq 第九题&#xff08;站队&#xff09;出了bug。。。 没有考虑到中位数会存在重叠错误问题&#xff08;直接卡掉5个点就离谱&#xff09; 后来发现其实第九题暴搜也能过。。。&#xff08;要是IOI赛制就能ak了&#xff09; 前面的水题不贴了 # 9.士兵站…

牛客题霸 [判断一个链表是否为回文结构] C++题解/答案

判断一个链表是否为回文结构 题目描述 给定一个链表&#xff0c;请判断该链表是否为回文结构。 题解&#xff1a; 直接将链表内的数据存入string中&#xff0c;然后从两端开始向中间判断即可 代码&#xff1a; /*** struct ListNode {* int val;* struct ListNode *next;…

HDU6736 F.Forest Program(dfs找环)

F.Forest Program 千摆渡题解 找环可以使用dfs一遍求出。 方法为&#xff1a;vis数组设置为三种状态&#xff0c;0表示未被访问过。1表示正在被访问&#xff0c;即边指向的结点是当前结点在dfs树上的祖先节点。2表示访问完毕。同时dfs的同时记录每一个结点的先驱pre。如果边访…

P7324-[WC2021]表达式求值【dp】

正题 题目链接:https://www.luogu.com.cn/problem/P7324 题目大意 给一个只包含mmm个值的表达式&#xff0c;<<<表前后取最小值&#xff0c;>>>表前后取最大&#xff0c;???可以是小于也可以是大于。 然后nnn次给出这mmm个值&#xff0c;所有方案下表达…