ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

LOJ #3341. 「NOI2020」时代的眼泪

2022-08-02 01:32:20  阅读:148  来源: 互联网

标签:LOJ maxS 3341 NOI2020 int maxn maxB include 散块


看题解不要在多个题解之间反复横跳!

题目叙述

平面上若干个点 \((i,p_i)\) ,其中 \(p_i\) 为一个 \(1\sim n\) 的排列,\(m\) 次询问,每次询问一个矩形内部点对满足一个在左下一个在右上的数量。

题解

直接分块。

散块对整块/散块的贡献

预处理 \(s_{i,j}\) 表示 \(1\sim i\) 这些块内,值 \(\le j\) 的数的数量。
每次枚举散块里面的点,差分即可。有的时候做分块要暴力一点,有人做这个部分的时候用的是离线然后在一个值域分块里面每次添加一个数,查询值在一个区间内的数的数量。

散块内部/整块内部

考虑将每个整块离散化,预处理处 \(s_{i,j}\) 表示这个散块 \(i\) 往前,\(\le j\) 数的数量。
然后每次询问直接二分一下什么的,\(\mathcal O(\sqrt{n})\) 复杂度查询一个散块就可以了。
整块内部也是类似的,因为只有 \(\mathcal O(n)\) 种本质不同的情况,直接预处理答案就可以了。

整块之间

考虑容斥,假设需要计算块构成的区间 \([l,r]\) 在值域 \([d,u]\) 内的逆序对数量。
那么直接转化为 \([l,r]\) 内在 \([1,u]\) 内的逆序对数两减去 \([l,r]\) 内在 \([1,d-1]\) 内的逆序对数再减去小的在 \([l,r],[1,d-1]\) ,大的在 \([l,r],[d,u]\) 的数量。

  • 前面相当于求形如 \([l,r]\) ,值域在 \([1,i]\) 的逆序对数量。这个可以考虑从小到大添加每一个数。相当于对这个数所在的块添加一个最大的数。那么贡献就是前面所有比这个数小的数的数量,这个可以通过我们计算散块预处理出来的东西计算他。这是每次修改需要做的事情,如果把第 \(x\) 个块对第 \(y\) 个块的贡献放在第 \(x\) 行 \(y\) 列,修改相当于修改一列,询问的时候枚举哪个块在后面,相当于查询一行之和,需要处理一下前缀和。
  • 后面这个,计算出每个块内值域在 \([1,d-1]\) 的数量有多少个,做一个前缀和,再枚举每个块查询值域在 \([d,u]\) 内的数的数量,与他前面在 \([1,d-1]\) 内的数的数量相乘,加起来就好了。

感觉这种题就很没意思。给我5h应该是可以做出来的。

总结

  • 分块题不要忘记一个块的大小只有 \(\sqrt{n}\) ,所以其实如果是至于区间询问的话只有 \(\mathcal O(n)\) 种。

代码

不是我的,是FZzzz的。

#include<algorithm>
#include<vector>
#include<cmath>
#include<cstdio>
#include<cctype>
using namespace std;
inline int readint(){
	int x=0;
	char c=getchar();
	bool f=0;
	while(!isdigit(c)&&c!='-') c=getchar();
	if(c=='-'){
		f=1;
		c=getchar();
	}
	while(isdigit(c)){
		x=x*10+c-'0';
		c=getchar();
	}
	return f?-x:x;
}
const int maxn=1e5+5,maxm=2e5+5,maxS=350,maxB=350;
int n,m,p[maxn],p2[maxn];
int S,B,L[maxB],R[maxB],pos[maxn];
int ord[maxn],s1[maxB][maxn],s2[maxB][maxS][maxS];
int lbd[maxB][maxn],ubd[maxB][maxn],s3[maxB][maxS][maxS];
bool cmp(int a,int b){
	return p[a]<p[b];
}
typedef long long ll;
ll ans[maxm];
int query1(int r1,int r2,int c1,int c2){
	return s1[r2][c2]-s1[r1-1][c2]-s1[r2][c1-1]+s1[r1-1][c1-1];
}
int query2(int x,int r1,int r2,int c1,int c2){
	return s2[x][r2][c2]-s2[x][r1-1][c2]-s2[x][r2][c1-1]+s2[x][r1-1][c1-1];
}
int query(int r1,int r2,int c1,int c2){
	int x=pos[r1];
	ll ans=0;
	for(int i=L[x];i<=R[x];i++)
		if(r1<=ord[i]&&ord[i]<=r2&&c1<=p[ord[i]]&&p[ord[i]]<=c2)
			ans+=query2(x,lbd[x][c1],i-L[x],r1-L[x]+1,ord[i]-L[x]);
	return ans;
}
struct qry{
	int l,r,id;
	bool flag;
	qry(int l,int r,int id,bool flag):l(l),r(r),id(id),flag(flag){}
};
vector<qry> q[maxn];
ll s4[maxB][maxB];
ll query4(int l,int r){
	ll ans=0;
	for(int i=l;i<=r;i++) ans+=s4[i][r]-s4[i][l-1];
	return ans;
}
int main(){
	#ifdef LOCAL
	freopen("in.txt","r",stdin);
	freopen("out.txt","w",stdout);
	#endif
	n=readint();
	m=readint();
	for(int i=1;i<=n;i++) p2[p[i]=readint()]=i;
	S=sqrt(n);
	B=(n-1)/S+1;
	for(int i=1;i<=B;i++){
		L[i]=(i-1)*S+1;
		R[i]=i==B?n:i*S;
		for(int j=L[i];j<=R[i];j++){
			pos[j]=i;
			s1[i][p[j]]++;
			ord[j]=j;
		}
		sort(ord+L[i],ord+R[i]+1,cmp);
		for(int j=1;j<=n;j++) s1[i][j]+=s1[i-1][j]+s1[i][j-1]-s1[i-1][j-1];
		for(int j=L[i];j<=R[i];j++) s2[i][j-L[i]+1][ord[j]-L[i]+1]=1;
		for(int j=1;j<=R[i]-L[i]+1;j++) for(int k=1;k<=R[i]-L[i]+1;k++)
			s2[i][j][k]+=s2[i][j-1][k]+s2[i][j][k-1]-s2[i][j-1][k-1];
		for(int j=1;j<=p[ord[L[i]]];j++) lbd[i][j]=1;
		for(int j=L[i];j<R[i];j++)
			for(int k=p[ord[j]]+1;k<=p[ord[j+1]];k++) lbd[i][k]=j-L[i]+2;
		for(int j=p[ord[R[i]]]+1;j<=n;j++) lbd[i][j]=R[i]-L[i]+2;
		for(int j=1;j<p[ord[L[i]]];j++) ubd[i][j]=1;
		for(int j=L[i];j<R[i];j++)
			for(int k=p[ord[j]];k<p[ord[j+1]];k++) ubd[i][k]=j-L[i]+2;
		for(int j=p[ord[R[i]]];j<=n;j++) ubd[i][j]=R[i]-L[i]+2;
		for(int j=1;j<=R[i]-L[i]+1;j++) for(int k=j;k<=R[i]-L[i]+1;k++)
			s3[i][j][k]=s3[i][j][k-1]+query2(i,j,k-1,1,ord[L[i]+k-1]-L[i]);
	}
	for(int i=0;i<m;i++){
		int r1,r2,c1,c2;
		r1=readint();
		r2=readint();
		c1=readint();
		c2=readint();
		if(pos[r1]==pos[r2]){
			ans[i]=query(r1,r2,c1,c2);
			continue;
		}
		ans[i]=query(r1,R[pos[r1]],c1,c2)+query(L[pos[r2]],r2,c1,c2);
		vector<int> res1,res2;
		for(int j=L[pos[r1]];j<=R[pos[r1]];j++)
			if(ord[j]>=r1&&c1<=p[ord[j]]&&p[ord[j]]<=c2)
				res1.push_back(p[ord[j]]);
		for(int j=L[pos[r2]];j<=R[pos[r2]];j++)
			if(ord[j]<=r2&&c1<=p[ord[j]]&&p[ord[j]]<=c2)
				res2.push_back(p[ord[j]]);
		int cur=0;
		for(int j=0;j<(int)res1.size();j++){
			while(cur<(int)res2.size()&&res2[cur]<res1[j]) cur++;
			ans[i]+=res2.size()-cur;
		}
		for(int j=r1;j<=R[pos[r1]];j++) if(c1<=p[j]&&p[j]<=c2)
			ans[i]+=query1(pos[r1]+1,pos[r2]-1,p[j]+1,c2);
		for(int j=L[pos[r2]];j<=r2;j++) if(c1<=p[j]&&p[j]<=c2)
			ans[i]+=query1(pos[r1]+1,pos[r2]-1,c1,p[j]-1);
		for(int j=pos[r1]+1;j<pos[r2];j++)
			ans[i]-=1ll*query1(j,j,1,c1-1)*query1(j+1,pos[r2]-1,c1,c2);
		for(int j=pos[r1]+1;j<pos[r2];j++)
			ans[i]+=s3[j][lbd[j][c1]][ubd[j][c2]-1];
		q[c2].push_back(qry(pos[r1]+1,pos[r2]-1,i,1));
		q[c1-1].push_back(qry(pos[r1]+1,pos[r2]-1,i,0));
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<pos[p2[i]];j++) s4[pos[p2[i]]][j]+=query1(1,j,1,i-1);
		for(int j=pos[p2[i]];j<=B;j++)
			s4[pos[p2[i]]][j]+=query1(1,pos[p2[i]]-1,1,i-1);
		for(int j=0;j<(int)q[i].size();j++)
			if(q[i][j].flag) ans[q[i][j].id]+=query4(q[i][j].l,q[i][j].r);
			else ans[q[i][j].id]-=query4(q[i][j].l,q[i][j].r);
	}
	for(int i=0;i<m;i++) printf("%lld\n",ans[i]);
	return 0;
}

标签:LOJ,maxS,3341,NOI2020,int,maxn,maxB,include,散块
来源: https://www.cnblogs.com/YouthRhythms/p/16542257.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有