题目描述:
一个二维平面直角坐标系,其中有 N 颗星星(坐标为整点),你会有 M 个询问,询问以某个整 点为顶点的正三角形包含大于等于 K 个星星的最小非负整数边长为多少,如果无 法满足输出-1。
对于一个 正三角形,如果给出的顶点为(X,Y),边长为 L(L>=0,当 L 为 0 的时候退化为一 个点),那么三个顶点坐标分别为(X,Y),(X+L/2,Y+L/2*sqrt(3)),(X+L,Y)。
N
,
M
≤
30000
,
0
≤
X
,
Y
≤
1
0
8
N,M\le30000,0\le X,Y\le10^8
N,M≤30000,0≤X,Y≤108
题目分析:
对于询问,显然可以二分答案,我们可以把所有二分一起进行,那么相当于需要进行 log 轮查询多个三角形里面各有多少个点
如果是查矩形,这很好做,扫描线树状数组就可以解决。
但是三角形?
于是膜拜了std。。
三角形可以这么表示:
(注意S2和S3射线的起点不在三角形上)
那么现在问题就变成了数一个夹角范围的点。
std给出的解法是这样的:
首先我们考虑能否数出在一条直线某一侧的点:
设我们询问的点为
(
A
,
B
)
(A,B)
(A,B),直线的斜率为
k
(
k
≥
0
)
k(k\ge0)
k(k≥0),那么在直线右侧的点
(
x
,
y
)
(x,y)
(x,y)需要满足:
k
∗
(
x
−
A
)
+
B
≥
y
k*(x-A)+B\ge y
k∗(x−A)+B≥y
转化一下变成:
k
x
−
y
≥
k
A
−
B
kx-y\ge kA-B
kx−y≥kA−B
也就是说我们将点
(
x
,
y
)
(x,y)
(x,y)改为
k
x
−
y
kx-y
kx−y,问题就变成了数轴上右边的点数。
如果是一个夹角范围
k
1
,
k
2
≥
0
k_1,k_2\ge0
k1,k2≥0,(根据
k
k
k的正负调整不等号的方向)
将点改为
(
k
1
x
−
y
,
k
2
x
−
y
)
(k_1x-y,k_2x-y)
(k1x−y,k2x−y)之后,就变成了矩形的二维数点问题!只需要数出
(
k
1
A
−
B
,
k
2
A
−
B
)
(k_1A-B,k_2A-B)
(k1A−B,k2A−B)右上角的点就可以了。
我自己yy了一个解法:
以形成夹角的两条射线的方向向量为基底改写点到原点的方向向量,这样就只需要统计每个询问点右上角的点了。
(这道题在算S2的时候由于起点不是整点可能会有一些精度问题。)
Code(std解法):
#include<bits/stdc++.h>
#define maxn 30005
using namespace std;
char cb[1<<18],*cs,*ct;
#define getc() (cs==ct&&(ct=(cs=cb)+fread(cb,1,1<<18,stdin),cs==ct)?0:*cs++)
template<class T>inline void read(T &a){
char c;while(!isdigit(c=getc()));
for(a=c-'0';isdigit(c=getc());a=a*10+c-'0');
}
const double SQRT3 = sqrt(3), eps = 1e-10;
int n,m,k,L[maxn],R[maxn],len[maxn],id[maxn],S1[maxn],S2[maxn],S3[maxn],ans[maxn];
double Y[maxn<<1];int cy;
struct Point{
double x,y; Point(){}
Point(double x,double y):x(x),y(y){}
bool operator < (const Point &p)const{return x<p.x;}
}p[maxn],p1[maxn],p2[maxn],p3[maxn],q[maxn],tq[maxn];
//awesome!!!
inline Point getp1(Point p){return Point(p.x-p.y/SQRT3,p.y);}
inline Point getp2(Point p){return Point(p.x-p.y/SQRT3,p.x+p.y/SQRT3);}
inline Point getp3(Point p){return Point(-p.y,p.x+p.y/SQRT3);}
//su ba ra sii!!!
int arr[maxn<<1];
inline void upd(int i){for(;i<=cy;i+=i&-i) arr[i]++;}
inline int qsum(int i){int s=0;for(;i;i-=i&-i) s+=arr[i];return s;}
bool cmp(int i,int j){return tq[i].x<tq[j].x;}
inline bool equal(double x,double y){return fabs(x-y)<eps;}
inline int getval(double x){
int i=lower_bound(Y+1,Y+1+cy,x)-Y;
return i<=cy&&equal(Y[i],x)?i:i-1;
}
void solve(Point *a,int *S,int flg){
for(int i=1;i<=m;i++) id[i]=i,Y[i]=tq[i].y;
sort(id+1,id+1+m,cmp),cy=m;
for(int i=1;i<=n;i++) Y[++cy]=a[i].y;
sort(Y+1,Y+1+cy);int tmpc=1;
for(int i=2;i<=cy;i++) if(!equal(Y[i],Y[i-1])) Y[++tmpc]=Y[i];
cy=tmpc,memset(arr,0,(cy+1)<<2);
for(int o=m,j=n;o>=1;o--){
int i=id[o]; if(flg==3) tq[i].x++;//!!!
for(;j>=1&&a[j].x>=tq[i].x-eps;j--) upd(cy-getval(a[j].y)+1);
int qy=getval(tq[i].y); if(flg>1) qy++;//!!!
S[i]=qsum(cy-qy+1);
}
}
int main()
{
freopen("count.in","r",stdin);
freopen("count.out","w",stdout);
read(n),read(m),read(k);
for(int i=1;i<=n;i++) read(p[i].x),read(p[i].y),p1[i]=getp1(p[i]),p2[i]=getp2(p[i]),p3[i]=getp3(p[i]);
for(int i=1;i<=m;i++) read(q[i].x),read(q[i].y),tq[i]=getp1(q[i]),R[i]=2e8,ans[i]=-1;
sort(p1+1,p1+1+n),sort(p2+1,p2+1+n),sort(p3+1,p3+1+n);
solve(p1,S1,1);
for(int t=1;t<=30;t++){
for(int i=1;i<=m;i++) len[i]=(L[i]+R[i])>>1;
for(int i=1;i<=m;i++) {double l=len[i]*0.5;tq[i]=getp2(Point(q[i].x+l,q[i].y+l*SQRT3));}
solve(p2,S2,2);
for(int i=1;i<=m;i++) tq[i]=getp3(Point(q[i].x+len[i],q[i].y));
solve(p3,S3,3);
for(int i=1;i<=m;i++)
if(S1[i]-S2[i]+S3[i]>=k) ans[i]=R[i]=len[i];
else if(L[i]<R[i]) L[i]=len[i]+1;
}
for(int i=1;i<=m;i++) printf("%d\n",ans[i]);
}