• [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(nsqrt{m}))

    对于这道题,我们如果使用普通莫队的话需要加一个堆来维护删除时的最大值,复杂度是(O(nsqrt{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
    ",Ans[i]);return 0;
    }
    
  • 相关阅读:
    工厂对象模式简介
    (转)HelloWorld CMake CMake中构建静态库与动态库及其使用
    C和C++混合编程
    Google glog 使用
    VS2013 越来越慢
    shell 的语法
    (十二)命令模式详解(故事版)
    (十一)外观模式详解(Service第三者插足,让action与dao分手)
    (十)装饰器模式详解(与IO不解的情缘)
    (九)模板方法模式详解(包含与类加载器不得不说的故事)
  • 原文地址:https://www.cnblogs.com/asuldb/p/12173383.html
Copyright © 2020-2023  润新知