HT-014 Div3 扫雷 题解 [ 绿 ] [ 二维差分 ]

分析

观察到是曼哈顿距离 \(\le r\) 的范围可以扫到,联想到如下图形:

image

左边是 \(r=1\) 可以扫到的范围,右边是 \(r=2\) 可以扫到的范围。

于是,我们只要对这样的图形在 \(1000*1000\) 的格子里差分一下就好了 。

但这样的复杂度是 \(O(nm)\) 的,会死的很惨。

优化

不难发现这个图形是一个旋转过 \(45°\) 的正方形,所以我们先把他转回来。

归纳法可以得到原先为 \((x,y)\) 的点会变换为 \((n+x-y,x+y-1)\)
严格证明有点忘了,记得好像是用一次函数或者几何全等一线三等角证的。

于是我们就把一个斜着的图形变正了。

接下来就是把这个正方形的四个顶点算出来并且变换一下,套一个二维差分的板子,很简单。

时间为 \(O(m)\) ,常数有点大。

另外,为了不特判边界情况,可以直接开 \(4000*4000\) 的数组,把变换后的点横纵坐标都加一个 \(1000\) ,就不用处理下标为负数的情况。出题人比较好心,给了 \(2000ms\)\(1024Mib\)

代码

#include <bits/stdc++.h>
using namespace std;
typedef pair<int,int> pi;
int n,m,f[5005][5005],q;
pi change(pi ori)
{
	int x=ori.first,y=ori.second;
	return {n+x-y+1100,x+y-1+1100};
}
bool check(pi t)
{
	int x=t.first,y=t.second;
	return (x>=1&&x<=2*n-1&&y>=1&&y<=2*n-1);
}
int main()
{
	freopen("game.in","r",stdin);
	freopen("game.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin>>n>>m;
	while(m--)
	{
		int x,y,r;
		cin>>x>>y>>r;
		pi zs=change({x-r,y});
		pi zx=change({x,y-r});
		pi ys=change({x,y+r});
		pi yx=change({x+r,y});
		f[zs.first][zs.second]++;
		f[zx.first+1][zx.second]--;
		f[ys.first][ys.second+1]--;
		f[yx.first+1][yx.second+1]++;
	}
	for(int i=1;i<=5000;i++)
	{
		for(int j=1;j<=5000;j++)
		{
			f[i][j]=f[i][j]+f[i-1][j]+f[i][j-1]-f[i-1][j-1];
		}
	}
	cin>>q;
	while(q--)
	{
		int x,y;
		cin>>x>>y;
		pi now=change({x,y});
		cout<<f[now.first][now.second]<<endl;
	}
	return 0;
}
posted @ 2024-07-06 17:06  KS_Fszha  阅读(8)  评论(0编辑  收藏  举报