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;
}

 

posted @ 2018-07-12 10:06  Zinn  阅读(107)  评论(0编辑  收藏  举报