P5046 [Ynoi2019 模拟赛] Yuno loves sqrt technology I

P5046 [Ynoi2019 模拟赛] Yuno loves sqrt technology I

询问区间逆序对,强制在线。

分块。

简单的来说就是预处理每个点到块首和块尾的贡献,还有块与块之间的贡献,还有前 i 个块对于值 j 的贡献。

询问的时候直接调用预处理的答案并归并两边散块即可。

代码:

#include<bits/stdc++.h>
using namespace std;
template <typename T>
inline void read(T &x){
	x=0;char ch=getchar();bool Cnt=false;
	while(!isdigit(ch)){if(ch=='-'){Cnt=true;}ch=getchar();}
	while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	x=Cnt?-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 ;
}
const int N=1e5+5,siz=317,B=320;
struct BIT{
	int c[N];
	inline void Add(int i,int x){for(;i<N;i+=i&-i)c[i]+=x;}
	inline int Ask(int i){int res=0;for(;i;i^=i&-i)res+=c[i];return res;}
}t;
int n,m,a[N],L[B],R[B],pos[N],blocks,pre[N],suf[N],Cnt[B][N];
int x[B],y[B],posl,posr,c[N],d[N];
long long Num[B][B],las;
pair<int,int> b[N];
inline int Merge(int*a,int*b,int nl,int nr){//归并 
	int posl=1,posr=1,res=0;
	while(posl<=nl&&posr<=nr){
		if(a[posl]<b[posr]) ++posl;
		else res+=nl-posl+1,++posr;
	}
	return res;
}
int main(){
	read(n),read(m);
	for(int i=1;i<=n;i++) read(a[i]);
	blocks=(n-1)/siz+1;
	for(int i=1;i<=blocks;i++) L[i]=R[i-1]+1,R[i]=i*siz;
	R[blocks]=n;
	for(int i=1;i<=n;i++) b[i]={a[i],i};
	for(int i=1;i<=blocks;i++){//预处理块内每一个位置到块首/块尾的逆序对个数 
		sort(b+L[i],b+R[i]+1);
		for(int j=L[i];j<=R[i];j++) pos[j]=i,c[j]=b[j].first,d[j]=b[j].second;
		int x=0;
		for(int j=L[i];j<=R[i];j++){
			t.Add(a[j],1);
			x+=t.Ask(n)-t.Ask(a[j]);
			pre[j]=x;
		}
		Num[i][i]=x;
		for(int j=L[i];j<=R[i];j++){
			suf[j]=x;
			t.Add(a[j],-1);
			x-=t.Ask(a[j]-1);
		}
	}
	sort(b+1,b+n+1);
	for(int j=1;j<=blocks;j++){//预处理前 i 个块中 小于等于/大于等于  j 的个数 
		for(int i=1,k=L[j];i<=n;i++){
			const int id=b[i].second;
			while(k<=R[j]&&b[i].first>c[k])++k;
			if(id<L[j]) Cnt[j][id]=k-L[j];
			else if(id>R[j]) Cnt[j][id]=R[j]-k-(k<=R[j]&&b[i].first==c[k])+1;
		}
	}
	for(int i=1;i<=blocks;i++) for(int j=2;j<=n;j++) Cnt[i][j]+=Cnt[i][j-1];
	for(int len=1;len<=blocks;len++){//预处理第 i 个块到第 j 个块的答案 
		for(int i=1;i<=blocks;i++)
		if(len+i>blocks) break;
		else{
			const int j=i+len;
			Num[i][j]=Num[i+1][j]+Num[i][j-1]-Num[i+1][j-1]+Cnt[j][R[i]]-Cnt[j][L[i]-1];
		}
	}
	while(m--){
		long long ql,qr;
		read(ql),read(qr);
		const int l=ql^las,r=qr^las,p=pos[l],q=pos[r];
		posl=posr=0;
		if(p==q){//如果在同一块,直接暴力归并 
			for(int i=L[p];i<=R[p];i++){
				if(l<=d[i]&&d[i]<=r) y[++posr]=c[i];
				else if(d[i]<l) x[++posl]=c[i];
			}
			las=pre[r]-((l==L[p])?(0):(pre[l-1]))-Merge(x,y,posl,posr);
		}
		else{
			las=Num[p+1][q-1]+pre[r]+suf[l];//先加上预处理的答案 :中间和中间,两边角内部 
			for(int i=p+1;i<q;i++) las+=(Cnt[i][R[p]]-Cnt[i][l-1])+(Cnt[i][r]-Cnt[i][L[q]-1]);//中间和两边角的贡献 
			for(int i=L[p];i<=R[p];i++) if(d[i]>=l) x[++posl]=c[i];//每个块的d数组是有序的 
			for(int i=L[q];i<=R[q];i++) if(d[i]<=r) y[++posr]=c[i];
			las+=Merge(x,y,posl,posr);//两边角之间归并暴力计算答案 
		}
		write(las),putchar('\n');
	}
	return 0;
}
posted @ 2021-04-15 23:38  __Anchor  阅读(52)  评论(0编辑  收藏  举报