Luogu8247 solution

Problem

link->https://www.luogu.com.cn/problem/P8247

Solution

我们不妨先来考虑,如果神箭游侠处于 \((1,1)\) 点,那么怎样的点 \((a_1,b_1)\)\((a_2,b_2)\) 才能满足他们处于以 \((1,1)\) 为端点的同一条射线上呢?

是的就是始他们与 \((1,1)\) 点处连接的线段对于经 \((1,1)\) 而垂直与 \(y\) 轴的直线而言角度相同即可:

如图,点 \(\texttt{A}\) 位于 \((1,1)\) 点,边 \(\texttt{AB}\) 与边 \(\texttt{AC}\) 对于点 \(\texttt{A}\) 而言角度相同(对于经 \((1,1)\) 而垂直与 \(y\) 轴的直线),则处于同一射线;\(\texttt{AD}\)\(\texttt{AF}\) 同理。

而对于两点点 \((x_1,y_1)\) 与点 \((x_2,y_2)\) 连接点 \((1,1)\) 所形成的的两条线段角度相同(重叠)的必要也是唯一条件是 \((x_1-1):(y_1-1)=(x_2-1):(y_2-1)\)

我们可以将点 \((x-1):(y-1)\) 约分最简比使用二元 bool 数组 \((\frac{x-1}{\gcd(x-1,y-1)},\frac{y-1}{\gcd(x-1,y-1)})\) 记录之。

回到问题。

我们说过上述算法是记录在神箭游侠处于 \((1,1)\) 点处的。为什么呢?因为这样便证了骷髅均在其的上、右、右上处,如果位置不一定,那么骷髅便可以在其的上、下、左上、右上、左下、右下四个方向了。

不过没关系,我们不妨把其分为四个部分:

其中红点代表着神箭游侠。

我们将左、上归于左上,将右归于右上,将下归于左下。

不妨设游侠坐标为 \((sx,sy)\),那么则对于四块部分我们可以用相同的代码分布计算:

  • 循环 \(x\)\(1\)\(sx\)\(y\)\(1\)\(sy\),如果 \((x,y)\) 有骷髅且 \((\frac{|x-sx|}{\gcd(|x-sx|,|y-sy|)},\frac{|y-sy|}{\gcd(|x-sx|,|y-sy|)})\) 在左下没有出现过,则将答案 \(+1\)

  • 循环 \(x\)\(sx+1\)\(n\)\(y\)\(1\)\(sy\),如果 \((x,y)\) 有骷髅且 \((\frac{|x-sx|}{\gcd(|x-sx|,|y-sy|)},\frac{|y-sy|}{\gcd(|x-sx|,|y-sy|)})\) 在右下没有出现过,则将答案 \(+1\)

  • 循环 \(x\)\(1\)\(sx\)\(y\)\(sy+1\)\(m\),如果 \((x,y)\) 有骷髅且 \((\frac{|x-sx|}{\gcd(|x-sx|,|y-sy|)},\frac{|y-sy|}{\gcd(|x-sx|,|y-sy|)})\) 在左上没有出现过,则将答案 \(+1\)

  • 循环 \(x\)\(sx+1\)\(n\)\(y\)\(sy+1\)\(m\),如果 \((x,y)\) 有骷髅且 \((\frac{|x-sx|}{\gcd(|x-sx|,|y-sy|)},\frac{|y-sy|}{\gcd(|x-sx|,|y-sy|)})\) 在右上没有出现过,则将答案 \(+1\)

Code

#include<bits/stdc++.h>
#define pd push_back
#define pb pop_back
#define mk make_pair
//#define int long long
#define PII pair<int,int>
#define _for(a,b,c) for(int a=b;a<=c;a++)
#define _rep(a,b,c) for(int a=b;a>=c;a--)
using namespace std;
template <typename T> inline void read(T& x) {
	x=0; T f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
	while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch&15); ch=getchar(); }
	x=x*f;
	return;
}
template <typename T,typename ...Arg> inline void read(T& x,Arg& ...arg){
	read(x); read(arg...);
}
int power(int a,int b) { 
	int ans=1;
	do {
		if(b&1) ans*=a; a*=a;
	} while(b>>=1);
	return ans;
}
const int N=1e3+5;
char mp[N][N];
int used[N][N],n,m,sx,sy,ans;
void Solve(int si,int ei,int sj,int ej) {
	memset(used,0,sizeof(used));
    //printf("(%d,%d)-(%d,%d)\n",si,sj,ei,ej);
	_for(i,si,ei)
		_for(j,sj,ej)
			if(mp[i][j]=='K') {
                //printf("(%d,%d) is K\n",i,j);
				int a=abs(i-sx),b=abs(j-sy);
				int d=__gcd(a,b);
				a/=d; b/=d;
                //printf("proportion of dist is (%d,%d)\n",a,b);
				if(!used[a][b])
					++ans,used[a][b]=1;
			}
}
signed main() {
	read(n,m);
	_for(i,1,n)
		_for(j,1,m) {
			cin>>mp[i][j]; if(mp[i][j]=='S') sx=i,sy=j;
		}
	Solve(1,sx,1,sy);
	Solve(sx+1,n,1,sy);
	Solve(1,sx,sy+1,m);
	Solve(sx+1,n,sy+1,m);
	printf("%d",ans);
	return 0;
}

但是,如果将这份代码交上去,你将会发现 \(\text{Subtask 4}\) 出现了奇怪的 RE,这是为什么呢?

可以发现,最后一处数据范围只保证 \(n\times m\le10^6\),而没有保证 \(n,m\le10^3\),也就是说可能会出现 \(n=10^6,m=1\) 这种畸形数据!

这时我们需要运用上一点哈希的手段:对于 \(x\le n,y\le m\),则唯一确定 \((x-1)\times m+y\) 且小于 \(n\times m\)。值得注意一点,运用这种哈希手段可能会导致 \((x-1)\) 出现负数,所以我们需要将其映射为 \(xm+y\)

Code

#include<bits/stdc++.h>
#define pd push_back
#define pb pop_back
#define mk make_pair
//#define int long long
#define PII pair<int,int>
#define _for(a,b,c) for(int a=b;a<=c;a++)
#define _rep(a,b,c) for(int a=b;a>=c;a--)
using namespace std;
template <typename T> inline void read(T& x) {
	x=0; T f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
	while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch&15); ch=getchar(); }
	x=x*f;
	return;
}
template <typename T,typename ...Arg> inline void read(T& x,Arg& ...arg){
	read(x); read(arg...);
}
int power(int a,int b) { 
	int ans=1;
	do {
		if(b&1) ans*=a; a*=a;
	} while(b>>=1);
	return ans;
}
const int N=2e6+5;
char mp[N];
int used[N],n,m,sx,sy,ans;
void Solve(int si,int ei,int sj,int ej) {
	memset(used,0,sizeof(used));
    //printf("(%d,%d)-(%d,%d)\n",si,sj,ei,ej);
	_for(i,si,ei)
		_for(j,sj,ej)
			if(mp[i*m+j]=='K') {
                //printf("(%d,%d) is K\n",i,j);
				int a=abs(i-sx),b=abs(j-sy);
				int d=__gcd(a,b);
				a/=d; b/=d;
                //printf("proportion of dist is (%d,%d)=%d\n",a,b,a*m+b);
				if(!used[a*m+b])
					++ans,used[a*m+b]=1;
			}
    //printf("Ans=%d\n",ans);
}
signed main() {
	read(n,m);
	_for(i,1,n)
		_for(j,1,m) {
			cin>>mp[i*m+j]; if(mp[i*m+j]=='S') sx=i,sy=j;
		}
	Solve(1,sx,1,sy);
	Solve(sx+1,n,1,sy);
	Solve(1,sx,sy+1,m);
	Solve(sx+1,n,sy+1,m);
	printf("%d",ans);
	return 1;
}

另:建议此题评黄。

posted @ 2022-08-01 14:22  lsj2009  阅读(21)  评论(0)    收藏  举报