题解:P1355 神秘大三角

· · 题解

这题一眼奔驰定理,但是发现题解区竟然没有用奔驰定理来解这道题的人。

(作为高中写有关向量的选填就喜欢用奔驰定理投机取巧的人很是失望bushi)

奔驰定理,顾名思义就是长得像奔驰标志的定理(其实是提出者名字里有奔驰bushi),具体内容如下图所示:

当然特殊的如果O点是三角形的五心,那奔驰定理就可以得出一些进阶定理,高考比较常用的就是当O点位于三角形重心(即三边中线交点)时,

(上式读者可以自行推导一下)

以下给出奔驰定理证明过程: 那么如何求出三角形面积呢?我们可以用高中数学学到的正弦定理,推导出2S=|a||b|*sin<a,b>(这里a,b为向量,<a,b>为向量a,b夹角) (面积不除二的原因是害怕精度误差,所以我们直接用两倍的面积,0向量x2照样是0向量)

talk is cheap

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define PII pair<int,int>
#define tul tuple<int,int,int>
#define all(x) x.begin(),x.end()
#define cy cout<<"Yes"<<endl
#define cn cout<<"No"<<endl
#define lc rt<<1
#define rc rt<<1|1
const int N=3e5+6,yyx=998244353;
int n,m;
struct node{
    int x,y;
    friend node operator- (node a,node b){
        return {a.x-b.x,a.y-b.y};
    }
    friend node operator+ (node a,node b){
        return {a.x+b.x,a.y+b.y};
    }
    friend bool operator== (node a,node b){
        if(a.x==0&&a.y==0||b.x==0&&b.y==0) return 1;
        int d=__gcd(abs(a.x),abs(a.y)),m=__gcd(abs(b.x),abs(b.y));
        return abs(a.x/d)==abs(b.x/m)&&abs(a.y/d)==abs(b.y/m)&&(a.x/d)*(a.y/d)==(b.x/m)*(b.y/m);
    }//这里算向量共线
    friend node operator* (node a,int b){
        return {a.x*b,a.y*b}; 
    }
}e[N];
int cross(node a,node b){
    return a.x*b.y-a.y*b.x;
}//叉积
int dot(node a,node b){
    return a.x*a.y+b.x*b.y;
}//点积
node toin(string &s){
    int x=0,y=0;
    for(int i=0;i<s.size();++i){
        if(s[i]=='('){
            int j=i+1,ok=0;
            if(s[j]=='-') j++,ok=1;
            while(j<s.size()&&(isdigit(s[j]))) j++;
            if(!isdigit(s[j])) j--;
            for(int k=i+1+ok;k<=j;++k) x=x*10+s[k]-48;
            if(ok) x*=-1;
            i=j;
        }
        if(s[i]==','){
            int j=i+1,ok=0;
            if(s[j]=='-') j++,ok=1;
            while(j<s.size()&&isdigit(s[j])) j++;
            if(!isdigit(s[j])) j--;
            for(int k=i+1+ok;k<=j;++k) y=y*10+s[k]-48;
            if(ok) y*=-1;
            i=j;
        }
    }
    return {x,y};
}
int check(int x,int y,node p){
    node a=e[x],b=e[y];
    node v1=p-a,v2=p-b;
   int xx=max(a.x,b.x),yy=max(a.y,b.y);
   int kx=min(a.x,b.x),ky=min(a.y,b.y);
   return v1==v2&&p.x>=kx&&p.y>=ky&&p.y<=yy&&p.x<=xx;
}
inline void solve(){
  string s[3];
  for(int i=0;i<3;++i) cin>>s[i];
  for(int i=1;i<=3;++i) e[i]=toin(s[i-1]);
  string st;cin>>st;
  node p=toin(st);
  int x=p.x,y=p.y;
  for(int i=1;i<=3;++i){
    if(e[i].x==x&&e[i].y==y){
        cout<<4<<endl;return;
    }
  }
  node v1=p-e[1],v2=p-e[2],v3=p-e[3];
  int sa=abs(cross(v3,v2)),sb=abs(cross(v1,v3)),sc=abs(cross(v1,v2));
  node b=v1*sa+v2*sb+v3*sc;//奔驰定理 sa*→a+sb*→b+sc*→c=→0;如果在点在边上也等于0,故需要先处理边上的点
  if(check(1,2,p)||check(2,3,p)||check(1,3,p)) cout<<3<<endl;//判边写的有点唐,其实直接看叉积等于0就行
  else if(b.x==0&&b.y==0) cout<<1<<endl;
  else cout<<2<<endl;
}
signed main(){
  cin.tie(0)->sync_with_stdio(0);
  //freopen("D://321//in.txt","r",stdin);
  //freopen("D://321//out.txt","w",stdout);
  int _=1;
  //cin>>_;
  while(_--)
  solve();
  return 0;
}