• 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('
    ');
    	}
    	return 0;
    }
    
  • 相关阅读:
    [转]linux下IPTABLES配置详解
    Linux查看物理CPU个数、核数、逻辑CPU个数 (转)
    linux的NetworkManager服务(转)
    iis 回收工作进程时出错的解决办法
    apache模块详解说明
    Apollo 刨析:简介
    Apollo 刨析:Localization
    格式化聊天列表时间
    ARGB 颜色取值与透明度搭配
    PHPExcel方法总结
  • 原文地址:https://www.cnblogs.com/Akmaey/p/14665089.html
Copyright © 2020-2023  润新知