题目大意
求区间加权众数。
分块大法好
显然我们可以用类似苹果树这道题的序列上问题的分块做法解决。
即初始化第i块到第j块的答案,以及每个元素在前i块出现的次数。
然后就很容易做了。
#include<cstdio>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fd(i,a,b) for(i=a;i>=b;i--)
using namespace std;
typedef long long ll;
const int maxn=100000+10;
int cnt[maxn],a[maxn],b[maxn],sum[1000][maxn];
ll ans[1000][1000];
int i,j,k,l,r,n,m,c;
ll t;
int main(){
scanf("%d%d",&n,&m);
fo(i,1,n) scanf("%d",&a[i]),b[i]=a[i];
sort(b+1,b+n+1);
l=unique(b+1,b+n+1)-b-1;
fo(i,1,n) a[i]=lower_bound(b+1,b+l+1,a[i])-b;
c=floor(sqrt(n))+1;
fo(i,1,(n-1)/c+1){
t=0;
fo(j,(i-1)*c+1,n){
cnt[a[j]]++;
t=max(t,(ll)cnt[a[j]]*b[a[j]]);
ans[i][(j-1)/c+1]=t;
}
fo(j,(i-1)*c+1,n) cnt[a[j]]--;
}
fo(i,1,(n-1)/c+1){
fo(j,1,n) sum[i][j]=sum[i-1][j];
fo(j,(i-1)*c+1,min(i*c,n)) sum[i][a[j]]++;
}
while (m--){
scanf("%d%d",&j,&k);
l=(j-1)/c+1;r=(k-1)/c+1;
if (l==r){
t=0;
fo(i,j,k){
cnt[a[i]]++;
t=max(t,(ll)cnt[a[i]]*b[a[i]]);
}
fo(i,j,k) cnt[a[i]]--;
printf("%lld\n",t);
continue;
}
t=0;
if (l+1<r) t=ans[l+1][r-1];
fo(i,j,l*c) cnt[a[i]]++;
fo(i,(r-1)*c+1,k) cnt[a[i]]++;
fo(i,j,l*c){
if (!cnt[a[i]]) continue;
ll x=sum[r-1][a[i]]-sum[l][a[i]]+cnt[a[i]];
x=(ll)x*b[a[i]];
t=max(t,x);
cnt[a[i]]=0;
}
fo(i,(r-1)*c+1,k){
if (!cnt[a[i]]) continue;
ll x=sum[r-1][a[i]]-sum[l][a[i]]+cnt[a[i]];
x=(ll)x*b[a[i]];
t=max(t,x);
cnt[a[i]]=0;
}
printf("%lld\n",t);
}
}