【计几】欧拉公式题集


POJ 2284-That Nice Euler Circuit

题意:给你一个可能会自交的多边形,问平面被分成了几份。

题解:计算几何 欧拉定理应用

思路:

  • 我们要求点数和边数,点数可以求交点,然后排序去重。

  • 边数的话,对于一条线段,其内部(不包括端点)每增加一个点,这个线段的贡献就会加一。

  • 因此可以枚举每条原始边,枚举所有点,判断是否可以分割线段。

AC代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<cmath>
using namespace std;

const int N=100100;
typedef long long LL;
int n;
const double eps = 1e-8;
int sign(double x){
    if(fabs(x) < eps) return 0;
    if(x < 0) return -1;
    return 1;
}
int dcmp(double x, double y){
    if(fabs(x - y) < eps) return 0;
    if(x < y) return -1;
    return 1;
}

struct point{
    double x,y;
    bool operator < (const point a)const{
        if(dcmp(x, a.x)) return x < a.x;
        return y < a.y;
    }
    bool operator == (const point a) const{ return !dcmp(x, a.x) && !dcmp(y, a.y); }
};

point operator + (point a,point b) { return {a.x + b.x, a.y + b.y};}
point operator - (point a,point b) { return {a.x - b.x, a.y - b.y};}
point operator * (point a,double b) { return {a.x * b, a.y * b};}
double cross(point a,point b) { return a.x * b.y - a.y * b.x; }
double dot(point a,point b) { return a.x * b.x + a.y * b.y; }
double area(point a,point b,point c) { return cross(b - a,c - a)/2; }
bool segment_intersection(point a1, point a2, point b1, point b2){//判断两个线段是否只有一个交点
    if(sign(cross(a2 - a1, b2 - b1))==0) return 0;
    double c1 = cross(a2 - a1, b1 - a1), c2 = cross(a2 - a1, b2 - a1);
    double c3 = cross(b2 - b1, a2 - b1), c4 = cross(b2 - b1, a1 - b1);
    return sign(c1) * sign(c2) <= 0 && sign(c3) * sign(c4) <= 0;
}
point get_line_intersection(point p, point v, point q, point w){
    point u = p - q;
    double t = cross(w, u) / cross(v, w);
    return p + v * t;
}
bool on_segment(point p, point a1, point a2){
    return !sign(cross(a1 - p, a2 - p)) && sign(dot(a1 - p, a2 - p)) < 0;
}

point P[N],q[N];
int cnt;

int main()
{
    int _ = 1;
    while(scanf("%d",&n)!=EOF)
    {
        cnt = 0;
        if(!n) break;
        for(int i=0; i< n;i ++)
        {
            scanf("%lf %lf",&P[i].x, &P[i].y);
            q[cnt++] = P[i];
        }
        n --;
        for(int i=0; i<n; i++)
            for(int j=i + 1; j < n; j++)
                if(segment_intersection(P[i], P[i + 1], P[j], P[j + 1]))
                    q[cnt++] = get_line_intersection(P[i], P[i + 1] - P[i], P[j], P[j + 1] - P[j]);
        sort(q, q + cnt);
        LL pos = unique(q, q + cnt) - q;

        LL e = n;
        for(int i=0; i<n; i++)
            for(int j=0; j<pos; j++)
                if(on_segment(q[j], P[i], P[i + 1])) e++;
        printf("Case %d: There are %lld pieces.\n", _++, 2 + e - pos);
    }

    // system("pause");
    return 0;
}

圆(circle)

题解:计算几何 欧拉定理应用

思路:

  • 研究一个圆的内接四边形,我们发现,一个内接四边形会多贡献一个交点,而且对于其两条对角线,都会被多贡献的那个交点分为两半。因此,一个内接四边形会多贡献一个交点 和 两条线段。

AC代码:

#include<iostream>
#include<cstdio>
using namespace std;

const int N=1e5+10;
typedef long long LL;

int main()
{
    LL n;
    // v = n + C(4,n) , e = n + C(2,n) + 2 * C(4,n)
    while(cin>>n)
    {
        cout<<1 + n * (n - 1) /2 + n * (n - 1) * (n - 2) * (n - 3) / 24<<endl;
    }

    system("pause");
    return 0;
}

Codeforces 933C C. A Colourful Prospect

题意:给你平面上的 n n n 个圆,问平面被分成了几份。

题解:Codeforces 933C+D C. A Colourful Prospect D. A Creative Cutout

思路:和第一题一样,求交点、去重、判断是否在圆上。注意两圆重合的情况。

AC代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<cmath>
using namespace std;

const int N=100100;
typedef long long LL;
const double eps = 1e-8;
int sign(double x){
    if(fabs(x) < eps) return 0;
    if(x < 0) return -1;
    return 1;
}
int dcmp(double x, double y){
    if(fabs(x - y) < eps) return 0;
    if(x < y) return -1;
    return 1;
}

struct point{
    double x,y;
    bool operator < (const point a)const{
        if(dcmp(x, a.x)) return x < a.x;
        return y < a.y;
    }
    bool operator == (const point a) const{ return !dcmp(x, a.x) && !dcmp(y, a.y); }
};

point operator + (point a,point b) { return {a.x + b.x, a.y + b.y};}
point operator - (point a,point b) { return {a.x - b.x, a.y - b.y};}
point operator * (point a,double b) { return {a.x * b, a.y * b};}
point operator / (point a,double b) { return {a.x / b, a.y / b};}
double cross(point a,point b) { return a.x * b.y - a.y * b.x; }
double dot(point a,point b) { return a.x * b.x + a.y * b.y; }
double area(point a,point b,point c) { return cross(b - a,c - a)/2; }
double length(point a) { return sqrt(a.x * a.x + a.y * a.y); }
point norm(point a) { return a / length(a); }
point rotate(point a,double rad) { return {a.x * cos(rad) -a.y * sin(rad), a.x * sin(rad) + a.y * cos(rad)}; }
bool segment_intersection(point a1, point a2, point b1, point b2){//判断两个线段是否只有一个交点
    if(sign(cross(a2 - a1, b2 - b1))==0) return 0;
    double c1 = cross(a2 - a1, b1 - a1), c2 = cross(a2 - a1, b2 - a1);
    double c3 = cross(b2 - b1, a2 - b1), c4 = cross(b2 - b1, a1 - b1);
    return sign(c1) * sign(c2) <= 0 && sign(c3) * sign(c4) <= 0;
}
point get_line_intersection(point p, point v, point q, point w){
    point u = p - q;
    double t = cross(w, u) / cross(v, w);
    return p + v * t;
}
bool on_segment(point p, point a1, point a2){
    return !sign(cross(a1 - p, a2 - p)) && sign(dot(a1 - p, a2 - p)) < 0;
}
double get_dis(point a, point b) { return sqrt((a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y)); }

int n;
point P[3],q[20];
int cnt;
double r[3];
int pa[3];
int sum[3];

int find(int x)
{
    if(pa[x]==-1) return x;
    return pa[x] == find(pa[x]);
}
void join(int a,int b)
{
    int xr = find(a), yr = find(b);
    if(xr!=yr) pa[xr] = yr;
}

int main()
{
    scanf("%d",&n);
    for(int i=0; i<n; i++)
    {
        scanf("%lf %lf %lf",&P[i].x, &P[i].y, &r[i]);
        P[i].x *=10000; P[i].y *=10000; r[i] *= 10000;
    }

    pa[0]=pa[1]=pa[2] = -1;

    int c = 0;
    for(int i=0; i<n; i++)
        for(int j=i + 1; j<n; j++)
        {
            double dis = get_dis(P[i], P[j]);
            if(dcmp(fabs(r[i] - r[j]), dis) <= 0 && dcmp(r[i] + r[j], dis) >= 0 && !(P[i] == P[j] && !dcmp(r[i], r[j])))
            {
                join(i, j);
                double rad = (r[i] * r[i] + dis * dis - r[j] * r[j]) / (2 * r[i] * dis);
                if(rad < -1) rad = -1;
                if(rad > 1 ) rad = 1;
                rad = acos(rad);
                q[cnt++] = P[i] + norm(rotate(P[j] - P[i], rad)) * r[i];
                q[cnt++] = P[i] + norm(rotate(P[j] - P[i], -rad)) * r[i];
            }
        }
    for(int i=0; i<n; i++) if(pa[i] == -1) c ++;

    sort(q, q + cnt);
    int pos = unique(q, q + cnt) - q;
    if(!cnt) pos = 0;

    for(int i=0; i < n; i++)
        for(int j=0; j<pos; j++)
            if(!dcmp(r[i], get_dis(P[i], q[j]))) sum[i]++;
            
    int e = 0, v = pos;
    for(int i=0; i<n; i++)
        if(!sum[i]) e++, v++;
        else e += sum[i];
    
    printf("%d\n",c + 1 + e - v);
    // cout<<c<<" "<<e<<" "<<v;

    // system("pause");

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值