[IOI2007]pairs 解题报告


https://www.luogu.com.cn/problem/P4648

观察题设方式,不难想到针对 \(B=1,2,3\) 单独作答。


\(B=1,O(N)\)

直接排序+双指针即可。

namespace ST1 {
	int a[N]; ll ans;
	void main(){
		for(int i=1;i<=n;i++)cin>>a[i];
		sort(a+1,a+n+1);
		for(int i=1,j=1;i<=n;i++){
			while(j<=n&&a[j]-a[i]<=d)j++;
			ans+=j-i-1;
		}
		cout<

\(B=2,O(N\log 3M)\)

在平面直角坐标系中,两个点 \((x_1,y_1),(x_2,y_2)\) 之间的曼哈顿距离 = \((x_1+y_1,x_1-y_1),(x_2+y_2,x_2-y_2)\) 之间的切比雪夫距离。

本题是一个不等式的限制,容易想到曼转切,将每个点 \((x,y)\to (x+y,x-y)\)

先讲一个暴力思路

我们其实就是要对每个点 \((x,y)\) 求平面上 \((x-d,y-d)\to (x+d,y+d)\) 子矩阵和 \(-1\)(因为会包含 \((x,y)\) 自身)。

更好的思路

可以沿用上面的,以 \(x\) 关键字排序+双指针(下面记左右指针为 \(i,j\)),原因是切比雪夫距离 \(\max(x_j-x_i,y_j-y_i)\le d\) 成立的条件之一是 \(x_j-x_i\le d\)
\(i,j\) 之间的 \(y\),每到一个就加入桶,则转为求值域上 \([x_i-d,x_i+d]\) 的和。容易用树状数组维护。注意值域可能为负,统一加上 \(M\) 即可。

namespace ST2 {
	int c[V+5]; ll ans;
	struct P{int x,y;}a[N];
	bool operator<(P a,P b){return a.x>a[i].x>>a[i].y,a[i].x+=a[i].y,a[i].y=a[i].x-2*a[i].y,a[i].x+=vm,a[i].y+=vm;
		sort(a+1,a+n+1);
		for(int i=1,j=1;i<=n;i++){
			add(a[i].y,-1);
			while(j<=n&&a[j].x-a[i].x<=d)add(a[j].y,1),j++;
			ans+=ask(min(a[i].y+d,3*vm))-ask(max(a[i].y-d,1)-1);
		}
		cout<

\(B=3,O(9M^2+N\cdot M)\)

当你从 \(B=2\) 看到 \(B=3\) 时,你会发现 \(M\) 一下子减到了 \(75\),这意味着 \(B=2\) 的暴力可以用在这里,其实是题目给我们减少了代码量!

\((x,y,z)\) 的前二维曼转切,并将每个点看成 \(z\) 平面上的点,平面与平面之间分开。
对于每个点 \((x_i,y_i,z_i)\),枚举另一个点的 \(z_j\),则所求即 \(z_j\) 平面上的 \((x_i-l,y_i-l)\to(x_i+l,y_i+l)\) 子矩阵和 \(-[z_i=z_j]\)
每个平面大小很小,预处理出二维前缀和。

namespace ST3 {
	int s[U][U][U]; ll ans;
	struct P{int x,y,z;}a[N];
	void main(){
		for(int i=1;i<=n;i++)cin>>a[i].x>>a[i].y>>a[i].z,a[i].x+=a[i].y,a[i].y=a[i].x-2*a[i].y,a[i].x+=vm,a[i].y+=vm,s[a[i].z][a[i].x][a[i].y]++;
		for(int c=1;c<=vm;c++)
			for(int i=1;i<=3*vm;i++)
				for(int j=1;j<=3*vm;j++)
					s[c][i][j]+=s[c][i-1][j]+s[c][i][j-1]-s[c][i-1][j-1];
		for(int i=1;i<=n;i++){
			for(int j=1;j<=vm;j++){
				int l=d-abs(a[i].z-j);
				if(l<0)continue;
				//(a[i].x-l,a[i].y-l)->(a[i].x+l,a[i].y+l)
				int X1=max(a[i].x-l,1),Y1=max(a[i].y-l,1),X2=min(a[i].x+l,3*vm),Y2=min(a[i].y+l,3*vm);
				ans+=s[j][X2][Y2]-s[j][X1-1][Y2]-s[j][X2][Y1-1]+s[j][X1-1][Y1-1]-(a[i].z==j);
			}
		}
		cout<

主函数

using namespace std;
const int N=1e5+5,V=75e4*3,U=75*3+5;
typedef long long ll;
int B,n,d,vm;
int main(){
	cin>>B>>n>>d>>vm;
	if(B==1)ST1::main();
	if(B==2)ST2::main();
	if(B==3)ST3::main();
}

易错点

  • long long
  • 加上 \(M\) 后值域为 \(3M\)