LG P5047 [Ynoi2019 模拟赛] Yuno loves sqrt technology II(莫队二次离线)

题意:给你一个长为 n 的序列 a,m 次询问,每次查询一个区间的逆序对数。

解题

区间逆序对的题很容易想到莫队,因为移动一个点可以查询当前区间内大于(如果移的是左端点就是小于)a[i]的数,就是当前询问的变化值,可以用树状数组O(logn)维护。
总复杂度O(\(n\sqrt{n}logn\)),复杂度不大行。
因此考虑再次将所有单点移动询问离线,
而且ans也满足可减性,ans(l,r)=ans(1,r)-ans(1,l-1)。
因此还是拆分成查询前缀,所有前缀中查询的东西相同。
现在就有了O(n)个插入和O(n\(\sqrt{n}\))个查询。
为了平均时间复杂度,做到O(1)查询而O(\(\sqrt{n}\))插入,使用值域分块。
按照值域进行分块每次修改可以修改O(\(\sqrt{n}\))个整块和O(\(\sqrt{n}\))个散块(整块可以标记永久化),即给大于(或小于)a[i]的数++。
而查询将整块标记和当前散块的值加起来即可。

代码

#include<bits/stdc++.h>

using namespace std;
int n,m;
int a[100005],b[100005],id[100005],id2[100005];
long long nd[100005],nd2[100005];
struct LS{
	int id;
	int val;
}LSH[100005];
bool cmp1(LS x,LS y){
	return x.val<y.val;
}
int g;
struct question{
	int l,r;
	int id;
}q[100005];
int qg;
bool cmp(question x,question y){
	return (id[x.l]!=id[y.l]?id[x.l]<id[y.l]:((id[x.l]&1)?x.r>y.r:x.r<y.r));
}
int t[100005];
int lowbit(int u){
	return u&(-u);
}
void add(int u,int w){
	while(u<=g){
		t[u]+=w;
		u+=lowbit(u);
	}
}
int Find(int u){
	int sum=0;
	while(u){
		sum+=t[u];
		u-=lowbit(u);
	}
	return sum;
}
struct nquestion{
	int l,r;
	int id;
	int type;
};
vector< nquestion > nqL[100005],nqR[100005];
long long lans[100005];
long long delta[100005];
struct fk{
	int val;
	int l,r;
}sum[100005];
int sum1[100005];
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
		LSH[i].val=a[i];
		LSH[i].id=i;
	}
	sort(LSH+1,LSH+1+n,cmp1);
	g=1;
	a[LSH[1].id]=1;
	for(int i=2;i<=n;i++){
		if(LSH[i].val!=LSH[i-1].val)g++;
		a[LSH[i].id]=g;
	}
	for(int i=1;i<=n;i++){
		nd[i]=i-Find(a[i])-1+nd[i-1];
		add(a[i],1);
	}
	for(int i=1;i<=n;i++)add(a[i],-1);
	for(int i=n;i>=1;i--){
		nd2[i]=Find(a[i]-1)+nd2[i+1];
		add(a[i],1);
	}
	nd2[0]=nd2[1];
	int siz=sqrt(n),siz2=sqrt(g);
	sum[0].l=1;
	for(int i=1;i<=n;i++)
		id[i]=i/siz;
	for(int i=1;i<=g;i++){
		id2[i]=i/siz2;
		if(id2[i]!=id2[i-1])sum[id2[i]].l=i;
		sum[id2[i]].r=i;
	}
	for(int i=1;i<=m;i++)
		scanf("%d%d",&q[i].l,&q[i].r),q[i].id=i;
	sort(q+1,q+1+m,cmp);
	int l=1,r=0;
	int dnm=0;
	for(int i=1;i<=m;i++){
		if(r<q[i].r){
			nqL[l].push_back((nquestion){r+1,q[i].r,i,-1});
			delta[i]+=nd[q[i].r]-nd[r];
			r=q[i].r;
		}
		if(r>q[i].r){
			nqL[l].push_back((nquestion){q[i].r+1,r,i,1});
			delta[i]-=nd[r]-nd[q[i].r];
			r=q[i].r;
		}
		if(l<q[i].l){
			nqR[r].push_back((nquestion){l,q[i].l-1,i,1});
			delta[i]-=nd2[l]-nd2[q[i].l];
			l=q[i].l;
		}
		if(l>q[i].l){
			nqR[r].push_back((nquestion){q[i].l,l-1,i,-1});
			delta[i]+=nd2[q[i].l]-nd2[l];
			l=q[i].l;
		}
	}
	for(int i=1;i<=n;i++){
		for(int j=0;j<nqL[i].size();j++){
			for(int k=nqL[i][j].l;k<=nqL[i][j].r;k++)
				delta[nqL[i][j].id]+=nqL[i][j].type*(sum[id2[a[k]]].val+sum1[a[k]]);
		}
		for(int j=0;j<id2[a[i]];j++)
			sum[j].val++;
		for(int j=sum[id2[a[i]]].l;j<a[i];j++)
			sum1[j]++;
	}
	memset(sum1,0,sizeof(sum1));
	for(int i=0;i<=id2[g];i++)sum[i].val=0;
	for(int i=n;i>=1;i--){
		for(int j=0;j<nqR[i].size();j++){
			for(int k=nqR[i][j].l;k<=nqR[i][j].r;k++)
				delta[nqR[i][j].id]+=nqR[i][j].type*(sum[id2[a[k]]].val+sum1[a[k]]);
		}
		for(int j=id2[a[i]]+1;j<=id2[g];j++)
			sum[j].val++;
		for(int j=a[i]+1;j<=sum[id2[a[i]]].r;j++)
			sum1[j]++;
	}
	for(int i=1;i<=m;i++)
		delta[i]+=delta[i-1],lans[q[i].id]=delta[i];
	for(int i=1;i<=m;i++)printf("%lld\n",lans[i]);
	return 0;
}
posted @ 2021-11-14 22:08  天穹の流星  阅读(71)  评论(0)    收藏  举报