bzoj4241 历史研究——分块
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4241
就是分块,预处理出从第 i 块到 j 位置的答案,以及从第 i 块到最后位置间每个数出现的次数;
然后块内统计、块外暴力即可。
代码如下:
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> using namespace std; typedef long long ll; int const maxn=1e5+5; int n,q,a[maxn],b[maxn],cnt[333][maxn],sta[maxn],top,m,tot,blk[maxn],num[maxn]; ll f[333][maxn]; int main() { scanf("%d%d",&n,&q); tot=sqrt(n); for(int i=1;i<=n;i++)blk[i]=(i-1)/tot+1; for(int i=1;i<=n;i++)scanf("%d",&a[i]),b[i]=a[i]; sort(b+1,b+n+1); m=unique(b+1,b+n+1)-b-1; for(int i=1;i<=n;i++)a[i]=lower_bound(b+1,b+m+1,a[i])-b; for(int i=1;i<=blk[n];i++) { ll nw=0; for(int j=lower_bound(blk+1,blk+n+1,i)-blk;j<=n;j++) cnt[i][a[j]]++,nw=max(nw,(ll)cnt[i][a[j]]*b[a[j]]),f[i][j]=nw;//cnt是第i块a[j]数量后缀 //f[i][j]表示第i块到j位置的答案 } for(int i=1,x,y;i<=q;i++) { scanf("%d%d",&x,&y); ll ans=f[blk[x]+1][y]; int st=lower_bound(blk+1,blk+n+1,blk[y])-blk; for(int i=st;i<=y;i++)num[a[i]]++,sta[++top]=a[i]; st=lower_bound(blk+1,blk+n+1,blk[x]+1)-blk; for(int i=x;i<st;i++) { num[a[i]]++; ans=max(ans,(ll)(cnt[blk[x]+1][a[i]]-cnt[blk[y]][a[i]]+num[a[i]])*b[a[i]]); sta[++top]=a[i]; } printf("%lld\n",ans); while(top)num[sta[top]]=0,top--;//不用memset } return 0; }