题目大意
给出N个坐标 (x,y) ( x , y ) ,并给出经过如下三个操作后的坐标 (x′,y′) ( x ′ , y ′ )
- (x,y)−−−−−>(x∗cosθ−y∗sinθ,x∗sinθ+y∗cosθ) ( x , y ) − − − − − > ( x ∗ cos θ − y ∗ sin θ , x ∗ sin θ + y ∗ c o s θ )
- (x,y)−−−−−>(x∗scale,y∗scale) ( x , y ) − − − − − > ( x ∗ s c a l e , y ∗ s c a l e )
- (x,y)−−−−−>(x+dx,y+dy) ( x , y ) − − − − − > ( x + d x , y + d y )
其中错误的结果坐标严格不超过一半
求
θ,scale,dx,dy
θ
,
s
c
a
l
e
,
d
x
,
d
y
N<=105,坐标范围[−109,109]
N
<=
10
5
,
坐
标
范
围
[
−
10
9
,
10
9
]
题解
相信你的眼睛,这题就是瞎搞。。
求四个未知数,根据数学老师说的,就要有四个方程
——然而一个点对只能提供两个方程,所以我们要枚举两点,再
O(N)
O
(
N
)
check
可是对于这么大的N,怎么办呢?
注意这句话:其中错误的结果坐标严格不超过一半
说明什么?
如果选排枚举,运气差一直找不到,就是
O(N3)
O
(
N
3
)
但是如果我们搞随机
rand
r
a
n
d
的话,会怎么样?
显然,我们随机一次选对点的概率为
12∗12=14
1
2
∗
1
2
=
1
4
,选错的概率就为
34
3
4
那么随机k次全错的概率为
(34)k
(
3
4
)
k
则随个50次全错的概率
(34)50<10−5
(
3
4
)
50
<
10
−
5
,一般只要RP不是负无穷基本就能搞出答案了,然后就OK了
复杂度
O(能快速过)
O
(
能
快
速
过
)
。。
就是以下代码有些问题,算出的
scale
s
c
a
l
e
的值有可能为负数,如果判断了就会TLE,非常神奇。。请巨佬指正
#include<cstdio>
#include<algorithm>
#include<cmath>
#define gt() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1000000,stdin),p1==p2)?EOF:*p1++)
#define db double
#define isnum(ch) (ch>='0'&&ch<='9')
using namespace std;
static char buf[1000000],*p1=buf,*p2=buf;
const int maxn=(1e5)+5;
const db eps=1e-4,Pi=3.1415926535897932384626433832795;
int n,N;db xita,sca,dx,dy,Cos,Sin;
struct ff{
db x,y,x_,y_;
}A[maxn];
db abs_(db x){return x<0?-x:x;}
struct hsh{
db x,y;
inline bool operator <(const hsh b)const{return x<b.x||(x==b.x&&y<b.y);}
inline bool operator ==(const hsh b)const{return abs_(x-b.x)<eps&&abs_(y-b.y)<eps;}
}tep[maxn],G;
bool check_easy(){
sort(tep+1,tep+1+n);
int max_l=0;
for(int i=1,j;i<=n;i=j){
j=i+1;
while(j<=n&&tep[j]==tep[i]) j++;
if(j-i>max_l) max_l=j-i,G=tep[i];
}
return (max_l>=N);
}
db read(){
db ret=0,d=1;bool f=0;char ch=gt();
while(!isnum(ch)) f|=(ch=='-'),ch=gt();
while(isnum(ch)) ret=ret*10+ch-'0',ch=gt();
if(ch=='.'){ch=gt();while(isnum(ch)) ret+=(ch-'0')/(d*=10),ch=gt();}
return f?-ret:ret;
}
inline int ran(){
static int seed=2333;
return seed=(int)(seed*2234578LL%2147483647);
}
void solve(int i,int j){
db a=A[i].x-A[j].x,b=A[i].y-A[j].y,c=A[i].x_-A[j].x_;
db a_=A[i].y-A[j].y,b_=A[i].x-A[j].x,c_=A[i].y_-A[j].y_;
db cos_k=(b_*c+b*c_)/(a*b_+a_*b),sin_k=(c_-a_*cos_k)/b_;
dx=A[i].x_-A[i].x*cos_k+A[i].y*sin_k,dy=A[i].y_-A[i].x*sin_k-A[i].y*cos_k;
xita=atan(sin_k/cos_k);
Cos=cos(xita),Sin=sin(xita);
sca=sin_k/Sin;
}
bool check(){
int cnt=0;
for(int i=1;i<=n;i++){
db X=A[i].x*Cos-A[i].y*Sin,Y=A[i].x*Sin+A[i].y*Cos;
X=X*sca+dx,Y=Y*sca+dy;
if(abs_(X-A[i].x_)<eps&&abs_(Y-A[i].y_)<eps) if(++cnt>=N) return 1;
}
return 0;
}
int main(){
n=read(),N=n+1>>1;
for(int i=1;i<=n;i++) A[i]=(ff){read(),read(),read(),read()},tep[i]=(hsh){A[i].x_-A[i].x,A[i].y_-A[i].y};
if(check_easy()) return printf("0\n1\n%.11lf %.11lf\n",G.x,G.y),0;
while(1){
int x=ran()%n+1,y=ran()%n+1;
while(x==y) y=ran()%n+1;
solve(x,y);
if(check()) return printf("%.11lf\n%.11lf\n%.11lf %.11lf\n",xita,sca,dx,dy),0;
}
return 0;
}
PS:解四元方程组据巨佬说可以用计算几何轻松搞,然后我不会,就老老实实解方程组了。。
我的方法是先设
p=cosθ∗scale,q=sinθ∗scale
p
=
cos
θ
∗
s
c
a
l
e
,
q
=
sin
θ
∗
s
c
a
l
e
利用二元一次方程组解出
p,q
p
,
q
然后就可以解出
dx,dy
d
x
,
d
y
而
q/p=tanθ
q
/
p
=
tan
θ
所以
θ=atan(q/p)
θ
=
a
t
a
n
(
q
/
p
)
最后
scale
s
c
a
l
e
也就好求了。。
NEW
现在搞定了!
当
scale
s
c
a
l
e
为负数时,对
θ
θ
不断加
π
π
直到
scale
s
c
a
l
e
为正为止,就OK了!!!
只要改改解方程:
void solve(int i,int j){
db a=A[i].x-A[j].x,b=A[i].y-A[j].y,c=A[i].x_-A[j].x_;
db a_=A[i].y-A[j].y,b_=A[i].x-A[j].x,c_=A[i].y_-A[j].y_;
db cos_k=(b_*c+b*c_)/(a*b_+a_*b),sin_k=(c_-a_*cos_k)/b_;
dx=A[i].x_-A[i].x*cos_k+A[i].y*sin_k,dy=A[i].y_-A[i].x*sin_k-A[i].y*cos_k;
xita=atan(sin_k/cos_k);
Cos=cos(xita),Sin=sin(xita);
sca=cos_k/Cos;
while(sca<0){
xita+=Pi;
Cos=cos(xita),Sin=sin(xita);
sca=cos_k/Cos;
}
}