码迷,mamicode.com
首页 > 其他好文 > 详细

bzoj 4241【历史的研究】

时间:2019-01-17 12:54:09      阅读:172      评论:0      收藏:0      [点我收藏+]

标签:include   回溯   排序   变量   bool   out   直接   style   研究   

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

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

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

  具体来说就是,我们按照块的序号来依次求答案,当前确定一个序号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;
}

 

bzoj 4241【历史的研究】

标签:include   回溯   排序   变量   bool   out   直接   style   研究   

原文地址:https://www.cnblogs.com/kgxw0430/p/10281518.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!