[Atcoder 1219]历史研究

题目

回滚莫队,大概是一种莫队的小trick

对序列分块,对于左右端点在同一个块里的直接暴力;其余的询问按照左端点所在块分类,一个块内按照右端点升序排序

我们维护一个指针\(rp\)记录当前右端点的位置,对于一个询问\([l,r]\),设其所在块的右端点为\(R\),我们将\(rp\)暴力移动到\(r\)的位置;由于右端点单调,移动过程只有加入,当移动到\(r\)的时候,我们再将\([l,R]\)内的数暴力加入,进行询问;询问完后要将\([l,R]\)内的数产生的影响撤销掉;做完一个块内的询问我们暴力清除所有贡献就好了

分析一下复杂度,设块大小为\(B\),那么右指针移动的暴力清桶的复杂度是\(O(\frac{n}{B}n)\),加入零散块和撤销的复杂度是\(O(Bm)\),所以在\(B=\frac{n}{\sqrt{m}}\)的时候复杂度是\(O(n\sqrt{m})\)

对于这道题,我们如果使用普通莫队的话需要加一个堆来维护删除时的最大值,复杂度是\(O(n\sqrt{m}\log n)\)的,回滚莫队只需要在暴力加入零散块时记录一下原来的最大值就好了

代码

#include<bits/stdc++.h>
#define re register
#define LL long long
#define max(a,b) ((a)>(b)?(a):(b))
const int maxn=1e5+5;
inline int read() {
    char c=getchar();int x=0;while(c<'0'||c>'9')c=getchar();
    while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+c-48,c=getchar();return x;
}
struct Ask{int l,r,rk;}q[maxn];
int B,n,m,sz,a[maxn],tax[maxn],b[maxn],id[maxn],num,lb[maxn],rb[maxn],tot;
LL Ans[maxn],ans,nw;
inline int cmp(const Ask &A,const Ask &B){return id[A.l]==id[B.l]?A.r<B.r:id[A.l]<id[B.l];}
inline void add(int x) {
    tax[a[x]]++;ans=max(ans,1ll*tax[a[x]]*b[a[x]]);
}
inline LL calc(int l,int r) {
    for(re int i=l;i<=r;i++) add(i);
    for(re int i=l;i<=r;i++) tax[a[i]]--;
    return ans;
}
int main() {
    //freopen("data.in","r",stdin);
    n=read();m=read();B=n/(sqrt(m)+1);
    for(re int i=1;i<=n;i++)a[i]=b[i]=read();
    std::sort(b+1,b+n+1),sz=std::unique(b+1,b+n+1)-b-1;
    for(re int i=1;i<=n;i++)a[i]=std::lower_bound(b+1,b+sz+1,a[i])-b;
    for(re int L=1,R;L<=n;L=R+1) {
        R=L+B-1;if(R>n)R=n;++num;
        for(re int i=L;i<=R;++i)id[i]=num;
        lb[num]=L,rb[num]=R;
    }
    for(re int i=1;i<=m;i++){
        q[++tot].l=read(),q[tot].r=read(),q[tot].rk=i;
        if(id[q[tot].l]==id[q[tot].r]) {
            Ans[i]=calc(q[tot].l,q[tot].r);ans=0;
            --tot;continue;
        }
    }
    std::sort(q+1,q+tot+1,cmp);q[tot+1].l=0;
    for(re int k=1,i=1;i<=tot;i++) {
        if(id[q[i].l]==id[q[i+1].l]) continue;
        int h=rb[id[q[i].l]],lp=h+1;ans=0;
        for(re int j=k;j<=i;++j) {
            while(lp<=q[j].r) add(lp++);
            nw=ans;int t=h;
            while(t>=q[j].l) add(t--);
            Ans[q[j].rk]=ans;ans=nw;
            while(t<h) tax[a[++t]]--;
        }
        while(lp>h+1) tax[a[--lp]]--;k=i+1;
    }
    for(re int i=1;i<=m;i++)printf("%lld\n",Ans[i]);return 0;
}

猜你喜欢

转载自www.cnblogs.com/asuldb/p/12173383.html