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;
}
另:建议此题评黄。