其他分享
首页 > 其他分享> > LOJ #3341. 「NOI2020」时代的眼泪

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

作者:互联网

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

题目叙述

平面上若干个点 \((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]\) 的数量。

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

总结

代码

不是我的,是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