P5047 [Ynoi2019 模拟赛] Yuno loves sqrt technology II

题目

P5047 [Ynoi2019 模拟赛] Yuno loves sqrt technology II

静态多次询问区间逆序对。

分析

二次离线莫队模板。

莫队是显然的,然后考虑怎么维护端点的移动,直接维护该怎么做:求当前区间多少个数比 \(x\) 大,多少个数比 \(x\) 小,可以离散化后直接值域树状数组。

这样做的复杂度 \(O(n\sqrt{n}logn)\)

考虑优化。

我们可以把每次的询问改成前缀和的形式:

\([1,r]\) 中多少个数比 \(r\) 大,减掉 \([1,l-1]\) 中多少个数比 \(r\) 大。

那么区间移动的话前者显然是个类似前缀和的东西,直接预处理,树状数组维护。

后者是一个定区间,我们可以考虑离线下来,把这个询问挂到 \(l-1\) 处,然后可以考虑值域分块,每次修改 \(O(\sqrt{n})\),给比自己大的数块内直接\(+1\),大块直接整块标记\(+1\)

这样做单次询问 \(O(1)\),直接调用当前块标记和当前位置的值之和即可。

\(Trick:\)值域分块可以实现\(O(\sqrt{n})\)单次修改,\(O(1)\)询问这个数的排名

代码

#include <bits/stdc++.h>
using namespace std;
template <typename T>
inline void read(T &x){
	x=0;char ch=getchar();bool f=false;
	while(!isdigit(ch)){if(ch=='-'){f=true;}ch=getchar();}
	while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	x=f?-x:x;
	return ;
}
template <typename T>
inline void write(T x){
	if(x<0) putchar('-'),x=-x;
	if(x>9) write(x/10);
	putchar(x%10^48);
	return ;
}
#define ll long long
const int N=1e5+5,INF=1e9+7;
int n,m,idx,block,siz;
int a[N],b[N],num1[N],num2[N];
ll Ans[N],sum1[N],sum2[N];
struct Que{int l,r,id;}Q[N];
struct Query{
	int l,r,tp,id;
	Query(int tp=0,int l=0,int r=0,int id=0):tp(tp),l(l),r(r),id(id){}
};
inline bool Cmp(const Que &x,const Que &y){return ((x.l/block)^(y.l/block))?x.l<y.l:x.r<y.r;}
int c[N];
inline void Add(int x,int v){
	for(;x<=n;x+=(x&(-x))) c[x]+=v;
	return ;
}
inline int Ask(int x){
	int res=0;
	for(;x;x-=(x&(-x))) res+=c[x];
	return res;
}
int bl[N],Pre[N],pre[N],L[N],R[N];
inline void Modify1(int x){
	if(Pre[bl[x]]) for(int i=L[bl[x]];i<=R[bl[x]];i++) pre[i]+=Pre[bl[x]];
	Pre[bl[x]]=0;
	for(int i=L[bl[x]];i<=x;i++) pre[i]++;
	for(int i=1;i<=bl[x]-1;i++) Pre[i]++;
	return ;
}
inline void Modify2(int x){
	if(Pre[bl[x]]) for(int i=L[bl[x]];i<=R[bl[x]];i++) pre[i]+=Pre[bl[x]];
	Pre[bl[x]]=0;
	for(int i=x;i<=R[bl[x]];i++) pre[i]++;
	for(int i=bl[x]+1;i<=siz;i++) Pre[i]++;
	return ;
}
vector<Query>vec1[N],vec2[N];
void Solve(){
	int cnt=sqrt(idx);siz=1;L[siz]=1;
	if(cnt*cnt<idx) cnt++;
	for(int i=1;i<=idx;i++){
		bl[i]=siz;
		if(i%cnt==0) R[siz]=i,L[++siz]=i+1;
	}
	R[siz]=idx;
	for(int i=1;i<=n;i++){
		const int len=vec1[i].size();
		for(int j=0;j<len;j++){
			const int id=vec1[i][j].id,tp=vec1[i][j].tp,l=vec1[i][j].l,r=vec1[i][j].r;
			for(int k=l;k<=r;k++) Ans[id]+=1ll*tp*(Pre[bl[a[k]+1]]+pre[a[k]+1]);
		}
		Modify1(a[i]);
	}
	memset(Pre,0,sizeof(Pre)),memset(pre,0,sizeof(pre));
	for(int i=n;i>=1;i--){
		const int len=vec2[i].size();
		for(int j=0;j<len;j++){
			const int id=vec2[i][j].id,tp=vec2[i][j].tp,l=vec2[i][j].l,r=vec2[i][j].r;
			for(int k=l;k<=r;k++) Ans[id]+=1ll*tp*(Pre[bl[a[k]-1]]+pre[a[k]-1]); 
		}
		Modify2(a[i]);
	}
	return ;
}
signed main(){
	read(n),read(m);block=355;
	for(int i=1;i<=n;i++) read(a[i]),b[i]=a[i];
	sort(b+1,b+n+1);
	idx=unique(b+1,b+n+1)-b-1;
	for(int i=1;i<=n;i++) a[i]=lower_bound(b+1,b+idx+1,a[i])-b;
	for(int i=1;i<=m;i++) read(Q[i].l),read(Q[i].r),Q[i].id=i;
	for(int i=1;i<=n;i++) num1[i]+=i-1-Ask(a[i]),Add(a[i],1),sum1[i]=sum1[i-1]+num1[i];
	memset(c,0,sizeof(c));
	for(int i=n;i>=1;i--) num2[i]+=Ask(a[i]-1),Add(a[i],1),sum2[i]=sum2[i+1]+num2[i];
	sort(Q+1,Q+m+1,Cmp);
	int l=1,r=0;
	for(int i=1;i<=m;i++){
		if(r<Q[i].r) Ans[Q[i].id]+=sum1[Q[i].r]-sum1[r],vec1[l].push_back(Query(-1,r+1,Q[i].r,Q[i].id));
		if(r>Q[i].r) Ans[Q[i].id]-=sum1[r]-sum1[Q[i].r],vec1[l].push_back(Query(1,Q[i].r+1,r,Q[i].id));
		r=Q[i].r;
		if(l<Q[i].l) Ans[Q[i].id]-=sum2[l]-sum2[Q[i].l],vec2[r].push_back(Query(1,l,Q[i].l-1,Q[i].id));
		if(l>Q[i].l) Ans[Q[i].id]+=sum2[Q[i].l]-sum2[l],vec2[r].push_back(Query(-1,Q[i].l,l-1,Q[i].id));
		l=Q[i].l;
	}
	Solve();
	for(int i=1;i<=m;i++) Ans[Q[i].id]+=Ans[Q[i-1].id];
	for(int i=1;i<=m;i++) write(Ans[i]),putchar('\n');
	return 0;
}
posted @ 2021-04-29 20:42  __Anchor  阅读(35)  评论(0编辑  收藏  举报