• 莫队二次离线学习小记


    NOI的时候遇到了个毒瘤的莫队相关。
    莫队二次离线可以做其中的部分分。


    例题:
    给出一个序列({a_i}),然后有若干个询问,每次询问([l,r])中的逆序对个数。
    (正好就是NOI2020D1T3的其中一档部分分。)
    序列长度为(n),询问个数为(m)(n)(m)同阶,所以后面分析时间复杂度的时候都用(n)来表示。

    朴素做法:
    显然可以莫队,设当前的指针移到([L,R]),用个树状数组装下([L,R])中的数,在左右指针移动的时候计算答案的变化量。
    时间复杂度为(O(nsqrt n lg n))


    更优秀的做法:可以做到(O(n sqrt n))
    假如我们要将([L,R])移动到([L,R+1]),这时候需要询问([L,R])中大于(a[R+1])的数的个数。
    我们把这个东西看成另一个问题:若干组询问,每次询问([l,r])中大于(或小于,后面省略不写)(v)的数的个数。这种询问一共有(O(nsqrt n))个。离线处理这些询问。
    这就是莫队二次离线的核心思想。


    接下来考虑如何快速计算这个东西。
    首先可以差分,变成询问([1,x])中大于(v)的数的个数。
    那么我们要维护支持如下操作的数据结构:

    1. 将某个数(v)加入可重集中,这个操作有(O(n))次。
    2. 询问可重集中多少个数大于(v),这个操作有(O(nsqrt n))次。

    注意到插入和询问次数的复杂度不同,所以可以尝试一下平衡规划:维护一个(O(sqrt n))修改、(O(1))查询的数据结构。简单分块即可。
    于是总的时间复杂度为(O(nsqrt n))


    不过,如果暴力存下每个询问,空间复杂度是(O(nsqrt n))的,可能会承受不住。
    尝试去优化。
    假设现在的区间在([L,R]),要变成([L,R'])(设(R<R'))。(其它的情况类似)
    为了方便后面叙述定义符号:([l,r]|[L,R])表示(sum_{xin [l,r],y in [L,R]} [a_x>a_y])
    贡献为(sum_{i=R+1}^{R'}[L,i-1]|[i,i]=sum_{i=R+1}^{R'}[1,i-1][i,i]-[1,L-1][R+1,R'])
    那可以把这个询问挂在(L-1)上,记下([R+1,R'])等信息。扫到(L-1)的时候处理,左边的可以预处理直接得到,右边的用上述数据结构维护。
    于是空间复杂度就(O(n))了。


    代码

    洛谷P5047

    using namespace std;
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cmath>
    #define N 100010
    #define SQ 410
    #define ll long long
    int n,m,k;
    int a[N],*p[N];
    bool cmpp(int *x,int *y){return *x<*y;}
    int B,bel[N],nb,L[SQ];
    struct Query{
    	int l,r,t;
    } q[N];
    bool cmpq(Query x,Query y){return bel[x.l]<bel[y.l] || bel[x.l]==bel[y.l] && x.r<y.r;}
    namespace TA{
    	int t[N],all;
    	void clear(){memset(t,0,sizeof(int)*(k+1));all=0;}
    	void add(int x,int c){all+=c;for (;x<=k;x+=x&-x) t[x]+=c;}
    	int query(int x){int r=0;for (;x;x-=x&-x) r+=t[x];return r;}
    };
    struct List{
    	struct EDGE{
    		int l,r,o,t;
    		EDGE *las;
    	} e[N];
    	int ne;
    	EDGE *last[N];
    	void insert(int x,int l,int r,int o,int t){
    		e[ne]={l,r,o,t,last[x]};
    		last[x]=e+ne++;
    	}
    } q0,q1;
    #define re(x) (n+1-(x)) 
    int sum0[N],sum1[SQ];
    void insert(int x){
    	for (int i=x;i>=L[bel[x]];--i)
    		sum0[i]++;
    	for (int i=bel[x]-1;i>=1;--i)
    		sum1[i]++;
    }
    ll ans2[N],ans[N];
    void work2(List &q){
    	static int f[N];
    	TA::clear();
    	for (int i=1;i<=n;++i){
    		f[i]=TA::all-TA::query(a[i]);
    		TA::add(a[i],1);
    	}
    	memset(sum0,0,sizeof(int)*(k+1));
    	memset(sum1,0,sizeof(int)*(nb+1));
    	for (int i=0;i<=n;++i){
    		if (i)
    			insert(a[i]-1);
    		for (List::EDGE *ei=q.last[i];ei;ei=ei->las){
    			ll s=0;
    			int l=ei->l,r=ei->r;
    			for (int x=l;x<=r;++x)
    				s+=f[x]-(sum0[a[x]]+sum1[bel[a[x]]]);
    			ans2[ei->t]+=ei->o*s;
    		}
    	}
    }
    void work(){
    	sort(q+1,q+m+1,cmpq);
    	int l=1,r=0;
    	for (int i=1;i<=m;++i){
    		int L=q[i].l,R=q[i].r;
    		if (r<R) q0.insert(l-1,r+1,R,1,i),r=R;
    		if (l>L) q1.insert(re(r+1),re(l-1),re(L),1,i),l=L;
    		if (r>R) q0.insert(l-1,R+1,r,-1,i),r=R;
    		if (l<L) q1.insert(re(r+1),re(L-1),re(l),-1,i),l=L;
    	}
    	work2(q0);
    	reverse(a+1,a+n+1);
    	for (int i=1;i<=n;++i)
    		a[i]=k+1-a[i];
    	work2(q1);
    	for (int i=1;i<=m;++i)
    		ans[q[i].t]=ans2[i]+=ans2[i-1];
    }
    int main(){
    	scanf("%d%d",&n,&m);
    	for (int i=1;i<=n;++i)
    		scanf("%d",&a[i]),p[i]=&a[i];
    	sort(p+1,p+n+1,cmpp);
    	for (int i=1,last=-1;i<=n;++i){
    		if (*p[i]!=last)
    			last=*p[i],++k;	
    		*p[i]=k;		
    	}
    	B=sqrt(n);
    	for (int i=1;i<=n;++i)
    		nb=bel[i]=(i-1)/B+1;
    	for (int i=n;i>=1;--i)
    		L[bel[i]]=i;
    	for (int i=1;i<=m;++i)
    		scanf("%d%d",&q[i].l,&q[i].r),q[i].t=i;
    	work();
    	for (int i=1;i<=m;++i)
    		printf("%lld
    ",ans[i]);
    	return 0;
    }
    
  • 相关阅读:
    CF-911E.Stack Sorting(栈)
    随机算法 && CodeForces
    CF-579D."Or" Game(或运算)
    CF-242E.XOR on Segment(异或线段树)
    莫队 && 洛谷 P1494 [国家集训队]小Z的袜子
    洛谷 P4168 [Violet]蒲公英(分块)
    分块 && 洛谷 P2801 教主的魔法
    启发式合并 && U41492 树上数颜色
    使用mysqlbinlog server远程备份binlog的脚本
    mysqldump备份过程中都干了些什么
  • 原文地址:https://www.cnblogs.com/jz-597/p/13598510.html
Copyright © 2020-2023  润新知