标签: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; }
标签:include 回溯 排序 变量 bool out 直接 style 研究
原文地址:https://www.cnblogs.com/kgxw0430/p/10281518.html