【题解】P12598 参数要吉祥
P12598 参数要吉祥
题意
给定长度为 \(n\) 的序列,\(m\) 次询问区间 \([l,r]\),记 \(c(x)\) 为在该区间中出现次数为 \(x\) 的数的种类数,求出最大的 \(x\times c(x)\)。
题解
知识点:莫队,值域分块。
启发:
-
利用值域分块单点修改来实现莫队 \(O(1)\) 移动指针。
-
利用莫队指针移动的性质来将值域分块更新复杂度均摊正确。
下文令 \(n,m\) 以及值域同阶,都用 \(n\) 表示。
小清新莫队。
进行莫队算法的过程中,修改的次数是 \(O(n\sqrt{n})\) 的,查询的次数是 \(O(n)\) 的。
首先有一个非常显然的 \(O(n\sqrt{n} \log_2 n)\) 做法,每次移动指针的时候拿 multiset 或者线段树修改,然后查询最值,但这显然不是复杂度最优的做法,他们单次询问可以做到 \(O(\log_2 n)\),但修改也同样是 \(O(\log_2 n)\) 的,和莫队结合没能均摊好时间复杂度。
考虑值域分块,每个块维护块内的最值。
对于每个询问,在指针移动的过程中,如果修改了一个块内的某个值,显然是不能立即更新的,先它所在的块编号扔到一个栈里,等指针完全移动到该询问后统一处理。
这样做时间复杂度是对的,下面进行证明:
首先,有 \(n\) 次询问,莫队算法将这 \(n\) 次询问按左端点分到了 \(\sqrt{n}\) 个长度为 \(\sqrt{n}\) 的块中。
对于一个块内的询问,左端点的活动范围始终在 \(\sqrt{n}\) 内,右端点单调递增,极端情况下活动范围为 \(n\)。
所以一个块内的所有询问总共至多造成 \(\sqrt{n}\) 个值域块的更新,\(\sqrt{n}\) 个值域块更新的总复杂度是 \(O(\sqrt{n}\times \sqrt{n})=O(n)\) 的。
而询问总共分了 \(\sqrt{n}\) 个块,所以最坏复杂度也是 \(O(n\sqrt{n})\) 的。
对于其他操作,值域分块的单点修改是 \(O(1)\) 的,查询是 \(O(\sqrt{n})\) 的,综上,用它可以和莫队结合做到 \(O(n\sqrt{n})\) 的复杂度。
#include<bits/stdc++.h>
using namespace std;
#define rep(i,l,r) for(int i=(l);i<=(r);++i)
#define per(i,l,r) for(int i=(r);i>=(l);--i)
#define pr pair<int,int>
#define fi first
#define se second
#define pb push_back
#define all(x) (x).begin(),(x).end()
#define sz(x) (int)(x).size()
#define bg(x) (x).begin()
#define ed(x) (x).end()
#define N 202505
#define C 450
const int lim=2e5;
int n,m,len,bl[N],a[N],ans[N],cnt[N];
namespace ds{
int len,c,bl[N],cc[N];
int st[C],ed[C],mx[C];
inline void init(){
len=sqrt(lim);
c=(lim-1)/len+1;
rep(i,1,c){
st[i]=ed[i-1]+1;
ed[i]=i*len;
}
ed[c]=lim;
rep(i,1,c){
rep(j,st[i],ed[i]){
bl[j]=i;
}
}
}
inline void ref(int k){
mx[k]=*max_element(cc+st[k],cc+ed[k]+1);
}
inline int ask(int l,int r){
int p=bl[l],q=bl[r],ans=0;
if(p==q){
rep(i,l,r){
ans=max(ans,cc[i]);
}
}
else{
rep(i,l,ed[p]){
ans=max(ans,cc[i]);
}
rep(i,st[q],r){
ans=max(ans,cc[i]);
}
rep(i,p+1,q-1){
ans=max(ans,mx[i]);
}
}
return ans;
}
}
using ds::cc;
using ds::ref;
using ds::ask;
struct node{
int l,r,i;
}b[N];
bitset<C>upd;
vector<int>tmp;
inline void add(int k){
int &num=cnt[a[k]];
if(!upd[ds::bl[num]]){
tmp.pb(ds::bl[num]);
upd[ds::bl[num]]=1;
}
cc[num]-=num;
num++;
cc[num]+=num;
if(!upd[ds::bl[num]]){
tmp.pb(ds::bl[num]);
upd[ds::bl[num]]=1;
}
}
inline void del(int k){
int &num=cnt[a[k]];
if(!upd[ds::bl[num]]){
tmp.pb(ds::bl[num]);
upd[ds::bl[num]]=1;
}
cc[num]-=num;
num--;
cc[num]+=num;
if(!upd[ds::bl[num]]){
tmp.pb(ds::bl[num]);
upd[ds::bl[num]]=1;
}
}
signed main(){
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n>>m;
len=sqrt(n);
ds::init();
int V=0;
rep(i,1,n){
cin>>a[i];
V=max(V,a[i]);
bl[i]=(i-1)/len+1;
}
rep(i,1,m){
b[i].i=i;
cin>>b[i].l>>b[i].r;
}
sort(b+1,b+1+m,[](node x,node y){
if(bl[x.l]!=bl[y.l]){
return x.l<y.l;
}
return x.r<y.r;
});
int l=1,r=0;
rep(i,1,m){
while(l<b[i].l){
del(l++);
}
while(l>b[i].l){
add(--l);
}
while(r<b[i].r){
add(++r);
}
while(r>b[i].r){
del(r--);
}
for(int x:tmp){
ref(x);
upd[x]=0;
}
tmp.clear();
ans[b[i].i]=ask(1,V);
}
rep(i,1,m){
cout<<ans[i]<<"\n";
}
return 0;
}

浙公网安备 33010602011771号