Walker——题解

6 篇文章 0 订阅
3 篇文章 0 订阅

题目大意

给出N个坐标 (x,y) ( x , y ) ,并给出经过如下三个操作后的坐标 (x,y) ( x ′ , y ′ )

  • (x,y)>(xcosθysinθ,xsinθ+ycosθ) ( x , y ) − − − − − > ( x ∗ cos ⁡ θ − y ∗ sin ⁡ θ , x ∗ sin ⁡ θ + y ∗ c o s θ )
  • (x,y)>(xscale,yscale) ( 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 的话,会怎么样?
显然,我们随机一次选对点的概率为 1212=14 1 2 ∗ 1 2 = 1 4 ,选错的概率就为 34 3 4
那么随机k次全错的概率为 (34)k ( 3 4 ) k
则随个50次全错的概率 (34)50<105 ( 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;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值