bzoj 4241【历史的研究】

  这道题是求一个区间最值的,而且并没有强制在线,空间也比较常规。所以这题你写分块或莫队都可以。

  但是猛地发现,求最值没法儿删除啊!莫队的删除操作该怎么办呢?想一下,你对询问排序之后,当前你左指针在询问左端点的左边,这时候你需要把经过的数的影响删去,但最大值并不好维护啊。所以普通的莫队思路无法解决此类问题。

  因此,我们引入了回滚莫队。回滚莫队,顾名思义,就是滚来滚去的莫队算法。你写写发现它真的是在滚来滚去。我们调整指针的时候,从左向右无法处理,那我们可以每次先让左指针尽可能靠右,把从左向右转化为从右向左啊。

  具体来说就是,我们按照块的序号来依次求答案,当前确定一个序号x,然后我们枚举以x这个块为左端点的所有询问,每次我们都把左指针回到x块的最右端,然后从右向左移动查找最大值。为了可以让答案有一个回溯的效果,我们每求一个询问的时候,先让右指针向右扫到需要达到的位置,求一个最值,此时这个最值是从L(即x块的最右端)到询问的右端点这一区间内的最值,然后我们再让左指针从右向左到达询问的位置,求出最值。之后,让左指针直接回到L,让当前最大值等于上次求的L到询问的右端点这一区间内的最值。不断进行这种操作,求出所有答案。

  细节还好,不过我今早又犯了几个sb错误,导致调了一早上,最后看着数据debug好长时间才发现问题,只是一个变量写错。。。

  所以说,写题一定要先分析好思路,写法算法流程,敲代码的时候一定要聚精会神,检查的时候尽量先眼看自己有什么sb错误,不要盲目debug。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e5+10;
struct node{int l,r,id;}q[N];ll ans[N];bool flag;
int s,t,n,m,belong[N],a[N],b[N],vis[N],cnt[N];ll pre[N],tmp,cur;
bool cmp(node a,node b){return belong[a.l]==belong[b.l]?a.r<b.r:a.l<b.l;}
bool CMP(node a,node b){return a.id<b.id;}
void recall(int now){
    vis[a[now]]--;
}
void update(int now){
    vis[a[now]]++;tmp=max(tmp,1LL*vis[a[now]]*b[a[now]]);
}
void discrete(){
    sort(b+1,b+n+1);
    int sz=unique(b+1,b+n+1)-b-1;
    for(int i=1;i<=n;++i) a[i]=lower_bound(b+1,b+sz+1,a[i])-b;
}
int main(){
    //freopen("a.in","r",stdin);
    //freopen("a.out","w",stdout);
    scanf("%d%d",&n,&m);
    s=sqrt(1.0*n*n/m);t=(n-1)/s+1;
    for(int i=1;i<=n;++i) scanf("%d",&a[i]),belong[i]=(i-1)/s+1,b[i]=a[i];
    for(int i=1;i<=m;++i) scanf("%d%d",&q[i].l,&q[i].r),q[i].id=i;
    discrete();
    sort(q+1,q+m+1,cmp);
    int k=1; 
    for(int i=1;i<=t;++i){ //枚举每一个块 接着应该枚举左端点在这个块内的每一个询问 
        memset(vis,0,sizeof(vis));
        memset(pre,0,sizeof(pre));
        while(belong[q[k].l]!=i&&k<m) i++;
        int l=min(i*s,n)+1,r=min(i*s,n);int L=l-1;
        tmp=0;cur=0;
        while(belong[q[k].l]==i){
            if(belong[q[k].r]==i){
                for(int j=q[k].l;j<=q[k].r;++j){
                    cnt[a[j]]=0;
                }
                for(int j=q[k].l;j<=q[k].r;++j){
                    cnt[a[j]]++;ans[q[k].id]=max(ans[q[k].id],1LL*cnt[a[j]]*b[a[j]]);
                }
                
            }else{
                while(r<q[k].r) update(++r);
                cur=tmp;
                while(l>q[k].l) update(--l);
                ans[q[k].id]=tmp;while(l<L+1) recall(l++);
                tmp=cur;
            }
            k++;
        }
    }
    for(int i=1;i<=m;++i) printf("%lld\n",ans[i]);
    return 0;
}

 

speech.gif posted on 2019-01-17 11:59 kgxpbqbyt 阅读( ...) 评论( ...) 编辑 收藏
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值