首页 > 其他分享 >数据结构-莫队二次离线

数据结构-莫队二次离线

时间:2023-05-01 11:55:13浏览次数:37  
标签:数据结构 ll 离线 sum2 now 莫队 sum

莫队二次离线

问题:给一个序列a,每次询问区间里面有几个逆序对

刚刚又睡了半小时,终于睡醒了

\(n,m\leq 1e5\)

实现

询问

首先想一想莫队:

对于初始询问区间[l,r],将右指针r扩展到r+1,对于答案的贡献就是[l,r]里面大于a[r+1]的数量,写成数学公式就是\(\sum_{i=l}^r(a[i]>a[r+1])\)

然后可以直接树状数组解决,时间复杂度就是\(O(n\sqrt nlog n)\),一下子就飞起来了

想一下差分,贡献变成:[1,r]里面有几个大于a[r+1]减去[1,l-1]里面大于a[r+1]的数量,同样的写成数学公式:\(\sum_{i=1}^{r}(a[i]>a[r+1])-\sum_{i=1}^{l-1}(a[i]>a[r+1])\)

对于整体情况来说就是\((\sum_{i=r+1}^{r^`}([1,i-1],[i,i]))-([1,l-1],[r+1,r^、])\)

其中([],[])表示两个区间内的逆序对数量(保证前面那个区间在后面那个区间前面)

对于被减数,只有n个情况,直接树状数组\(O(nlogn)\)预处理掉,然后前缀和计算

对于减数,将r+1记在r和l-1上,从左往右去扫描t端点,同时维护,然后处理每次到t上面的询问,相当于n次插入和\(n\times \sqrt n\)次询问。可以在插入时对于值域分块,维护块内和块与块间的前缀和

对于[l,r]端点向着其他方向乱动也是一样的,所以不再讨论

这里的二次离线体现在的地方就是减数的求解,将问题区间差分后,再次将未知端点绑在已确定位置上,然后在遍历的时候顺手更新

代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll M=1e5+10;
ll n,m,a[M],lsh[M],cnt,pos[M],lim,lazy[M],sum[M<<2];
ll sum1[M],sum2[M],f1[M],f2[M],now_ans,ans[M];//1是前缀,2是后缀 
struct Q{
	ll l,r,id;
}q[M];
vector<Q> L[M],R[M];
bool cmp(Q x,Q y){
	if(pos[x.l]==pos[y.l])return x.r<y.r;
	else return pos[x.l]<pos[y.l];
}
ll lowbit(ll x){
	return x&(-x);
}
void add(ll x,ll val){
	for(;x<=cnt;x+=lowbit(x))sum[x]+=val;
}
ll ask(ll x){
	ll res=0;
	for(;x;x-=lowbit(x))res+=sum[x];
	return res;
}
void init(){//处理形如前缀和的逆序对,减数
	memset(sum,0,sizeof sum);
	memset(lazy,0,sizeof lazy);
	for(ll i=0;i<n;i++){
		for(auto now:L[i]){
			for(ll j=now.l;j<=now.r;j++){
				f1[now.id]+=sum[a[j]]+lazy[pos[a[j]]];
			}
		}
		for(ll j=a[i+1]-1;j>=lim*(pos[a[i+1]]-1)+1;j--)sum[j]++;
		for(ll j=1;j<=pos[a[i+1]]-1;j++)lazy[j]++;//处理整块前缀和 
	}
	memset(sum,0,sizeof sum);
	memset(lazy,0,sizeof lazy);
	for(ll i=n+1;i>=2;i--){
		for(auto now:R[i]){
			for(ll j=now.l;j<=now.r;j++){
				f2[now.id]+=sum[a[j]]+lazy[pos[a[j]]];
			}
		}
		for(ll j=a[i-1]+1;j<=lim*pos[a[i-1]];j++)sum[j]++;
		for(ll j=pos[a[i-1]]+1;j<=pos[cnt];j++)lazy[j]++;
	}
}
void remove(ll now){//莫队 
	if(q[now].r>q[now-1].r)now_ans+=sum1[q[now].r]-sum1[q[now-1].r]-f1[now];
	if(q[now].r<q[now-1].r)now_ans-=sum1[q[now-1].r]-sum1[q[now].r]-f1[now];
	if(q[now].l>q[now-1].l)now_ans-=sum2[q[now-1].l]-sum2[q[now].l]-f2[now];
	if(q[now].l<q[now-1].l)now_ans+=sum2[q[now].l]-sum2[q[now-1].l]-f2[now];
}
int main(){
	scanf("%lld%lld",&n,&m);
	lim=sqrt(n);
	for(ll i=1;i<=n;i++)pos[i]=(i-1)/lim+1;
	for(ll i=1;i<=n;i++){
		scanf("%lld",&a[i]);
		lsh[i]=a[i];
	}
	sort(lsh+1,lsh+1+n);
	cnt=unique(lsh+1,lsh+1+n)-lsh-1;
	for(ll i=1;i<=n;i++)a[i]=lower_bound(lsh+1,lsh+1+cnt,a[i])-lsh;
	//预处理被减数 
	for(ll i=1;i<=n;i++){
		sum1[i]=sum1[i-1]+ask(cnt-a[i]);
		add(cnt-a[i]+1,1);
	}
	memset(sum,0,sizeof sum);
	for(ll i=n;i>=1;i--){
		sum2[i]=sum2[i+1]+ask(a[i]-1);
		add(a[i],1);
	}
	for(ll i=1;i<=m;i++){
		scanf("%lld%lld",&q[i].l,&q[i].r);
		q[i].id=i;
	}
	sort(q+1,q+1+m,cmp);
	q[0].l=1,q[0].r=0;
	for(ll i=1;i<=m;i++){//将差分后的询问挂在节点上 
		if(q[i].r>q[i-1].r)L[q[i-1].l-1].push_back((Q){q[i-1].r+1,q[i].r,i});
		if(q[i].r<q[i-1].r)L[q[i-1].l-1].push_back((Q){q[i].r+1,q[i-1].r,i});
		if(q[i].l>q[i-1].l)R[q[i].r+1].push_back((Q){q[i-1].l,q[i].l-1,i});
		if(q[i].l<q[i-1].l)R[q[i].r+1].push_back((Q){q[i].l,q[i-1].l-1,i});
	}
	init();
	for(ll i=1;i<=m;i++){
		remove(i);
		ans[q[i].id]=now_ans;
	}
	for(ll i=1;i<=m;i++)printf("%lld\n",ans[i]);
	return 0;
}

标签:数据结构,ll,离线,sum2,now,莫队,sum
From: https://www.cnblogs.com/Zhangrx-/p/17366303.html

相关文章

  • 【数据结构】栈
    1 前言这节我们来看看计算机中的常见的栈结构。2 栈定义栈是一个后进先出(LastInFistOut,LIFO)的线性表,它要求只在表尾进行删除和插入操作。所谓的栈,其实就是一个特殊的线性表(顺序表、链表),但是它在操作上有一些特殊的要求和限制:栈的元素必须“后进先出”栈的操作只能......
  • 数据结构与算法复习--(2)
    算法和算法分析算法的定义对特定问题求解方法和步骤的一种描述,它是指令的有限序列。其中每个指令表示一个或多个操作。算法的描述自然语言:英语、中文流程图:传统流程图、NS流程图伪代码:类语言:类C语言程序代码:C语言程序、Java语言程序算法与程序算法是解决问题的一......
  • 【数据结构】链式型存储结构-双向链表
    1 前言只要大家坐过火车,对于双向链表的理解就相当简单。双向链表就是在单链表的基础之上,为每一个结点增加了它的前继结点,我们来看看。2 双向链表双向链表的定义如下:typedefstructDaulNode{ElemTypedata;structDaulNode*prior;//前驱结点structDa......
  • 【数据结构】链式型存储结构-循环单链表
    1 前言对于单链表,由于每个结点只存储了向后的指针,到了尾部标识就停止了向后链的操作。也就是说,按照这样的方式,只能索引后继结点不能索引前驱结点。这样一来,不从头结点出发,这样就无法访问到全部结点。为了解决这个问题,我们只需要将单链表的尾结点的指针由空指针改为指向头结点......
  • 【数据结构】链式型存储结构-静态链表
    1 前言地球人都知道C语言是个伟大的语言,它的魅力在于指针的灵活性,使得它可以非常容易地操作内存中的地址和数据,这比其他高级语言更加灵活方便。(面向对象语言,比如java,可以使用对象引用机制间接地实现指针的某些功能)但是古人还是木有C语言丫,木有JAVA丫,只有原始的Basic,Fortran等......
  • 【数据结构】链式型存储结构-单链表
    1 前言线性表的链式存储结构的特点就是用一组任意的存储单元存储线性表的数据元素,这组存储单元可以在内存中未被占用的任意位置。比起顺序存储结构每个元素只需要存储一个位置就可以了。现在链式存储结构中,除了要存储数据信息外,还要存储它的后继元素的存储地址(指针)。也就是说......
  • 【数据结构】线性表分类以及顺序型存储结构
    1 什么是线性表线性表的定义:由零个或多个数据元素组成的有限序列首先它是一个序列,也就是说元素之间是有先来后到之分。若元素存在多个,则第一个元素无前驱,而最后一个元素无后继,其他元素都有且只有一个前驱和后继。线性表强调是有限的,事实上无论计算机发展到多强大,他所能处理......
  • 分块思想基础莫队
    分块将数组分成sqrt(n)块,每次进行区间操作或者查询的时候,对于完整的块可以通过预处理的信息o1得到,不完整的块直接暴力跑,所以最坏复杂度是sqrt(n)。分块模板constintN=100010,B=sqrt(N);intblock;intst[B],ed[B],bel[N];intsum[B],tag[B];inta[N],sz[B];vo......
  • 数据结构与算法基础复习--(1)
    基本术语1.数据(Data)数据是能输入计算机且能被计算机处理的各种符号的集合信息的载体是对客观事物符号化的表示能够被计算机识别、存储和加工包括:数值型的数据:整数、实数等非数值型的数据:文字、图像、图形、声音等2.数据元素数据元素是数据的基本单位,在计......
  • 数组模拟实现数据结构
    数组模拟链表实现①单链表:邻接表(存储图和树)②双链表:优化某些问题单链表inte[N]存储val,intne[N]存储next//单链表模板inthead,e[N],ne[N],idx;//head表示头节点的下标,e[i]表示节点i的值,ne[i]表示节点i的指针是多少,idx存储当前已经用到了哪个点v......