【计几】杂题


POJ3304 Segments【从结果出发,反推解的存在情况】

题意:问是否存在一条直线经过给定的n条线段(非规范相交)。

思路:这道题要先从结果出发,发现任意解都可以转化为经过两个端点的解,则可以从此出发。

题解:POJ3304

代码:没有过,以后更。


HOJ-Super Shuttle Super Shuttle

题意:给一个定点和一堆圆,请你画一个过定点的圆,使得它与尽可能多的圆相交。

思路:与上题大同小异。

题解:【HUNNU11563】Super Shuttle(圆的反演)(扫描线)


第 45 届 ICPC 昆明 - Mr. Main and Windmills【从状态变化点(反转点)出发,反推解的转移情况】

思路:

  • 如果我们直接从乘客的路径上考虑,路径是连续的,很难找到答案。

  • 如果我们从反转点考虑,会发现只有在反转点,排名才会变化,这是离散的。因此n^2预处理所有反转点,O(1)查询即可。

题解:Mr. Main and Windmills (2020icpc昆明 线段相交)

AC代码:

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

typedef long long LL;
const int N=1100;
const double eps = 1e-9;
const double pi = acos(-1);

int dcmp(double x, double y){
    if(fabs(x - y) < eps) return 0;
    if(x < y) return -1;
    return 1;
}
int sign(double x){
    if(fabs(x) < eps) return 0;
    if(x < 0) return -1;
    return 1;
}

struct point{
    double x, 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 length(point a) { return sqrt(a.x * a.x + a.y * a.y); }
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); }
double project(point a, point b, point c) { return dot(b - a, c - a) / length(b - a); }
double angle(point a) { return atan2(a.y, a.x); }
point rotate(point a, double rad) { return {a.x * cos(rad) - a.y * sin(rad), a.x * sin(rad) + a.y * cos(rad)}; }
point norm(point a) { return a / length(a); }
double get_dis(point a, point b) { return length(a - b); }


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 a, point b, point c){
    return sign(dot(c - a, c - b)) < 0;
}

point st, ed;
bool operator < (const point a, const point b){
    double A = get_dis(a, st), B = get_dis(b, st);
    return A < B;
}


point P[N];
point inter[N][N];
int cnt[N];



int main()
{
    int n, m;
    scanf("%d %d", &n, &m);
    scanf("%lf %lf %lf %lf", &st.x, &st.y, &ed.x, &ed.y);

    for(int i=1; i<=n; i++) scanf("%lf %lf", &P[i].x, &P[i].y);

    for(int i=1; i<=n; i++)
    {
        for(int j=1; j<=n; j++)
            if(i != j)
            {
                point now = get_line_intersection(st, ed - st, P[i], P[j] - P[i]);
                if(on_segment(st, ed, now)) inter[i][++cnt[i]] = now;
            }
        sort(inter[i] + 1, inter[i] + cnt[i] + 1);
    }

    int id, k;
    while(m--)
    {
        scanf("%d %d", &id, &k);
        if(k > cnt[id]) printf("-1\n");
        else{
            point ans = inter[id][k];
            printf("%.7f %.7f\n", ans.x, ans.y);
        }
    }


    system("pause");
    return 0;
}

2021 icpc 网络赛第二场 Nearest Point【从状态变化角度(反转角度)出发,反推解的转移情况】

思路:

  • 和上一题一样。如果我们直接从360°的角度考虑,角度是连续的,很难找到答案。

  • 如果我们从反转角度考虑,会发现只有在反转角度,最近点才会变化,这是离散的。因此对于每个点i,找出所有反转角度,取中点考虑即可。

踩坑:

  • 1 . find函数里旋转后的点要把i点剔除,否则返回的最近点编号可能为本身。

  • 2 . 对于样例三,我原先写的代码在vscode和pta clang++可以输出正确答案,在devc++ 和 pta g++就输出不了正确答案。主要在divi[cnt++] = divi[cnt - 1] + pi; 这一句话,在devc++是先++,后再调用cnt。修改之后在devc++就可以输出正确答案。

  • 貌似devc++使用的是g++,vscode使用的是clang++。反正以后不要用会受到编译器差异影响的语句。

可优化:以下代码时间为1000ms左右,群友写的是80ms左右。

群友题解:The 2021 ICPC Asia Regionals Online Contest (II)

请添加图片描述

AC代码:

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

typedef long long LL;
const int N=60;
const double eps = 1e-11;
const double pi = acos(-1);

int dcmp(double x, double y){
    if(fabs(x - y) < eps) return 0;
    if(x < y) return -1;
    return 1;
}
int sign(double x){
    if(fabs(x) < eps) return 0;
    if(x < 0) return -1;
    return 1;
}

struct point{
    double x, y;
    int id;
    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 angle(point a) { return atan2(a.y, a.x); }
point rotate(point a, double rad) { return {a.x * cos(rad) - a.y * sin(rad), a.x * sin(rad) + a.y * cos(rad)}; }


point P[N];
point tmp[N];
double divi[N * N * 4];
int cnt;
double ans[N][N];


void add(point v)
{
    if(v.x < 0) v.x = -v.x, v.y = -v.y;
    divi[cnt++] = pi / 2 - angle(v);
    divi[cnt] = divi[cnt - 1] + pi, cnt++; // 原句是 divi[cnt++] = divi[cnt - 1] + pi;
}

double idx;
bool cmp(const point a, const point b){
    double A = fabs(a.x - idx), B = fabs(b.x - idx);
    if(dcmp(A, B)) return dcmp(A, B) < 0;
    return a.id < b.id;
}

int n;
int find(double rad, int i)
{
    int pos = 0;
    for(int j=0; j<n; j++)
        if(j != i)
            tmp[pos] = rotate(P[j], rad), tmp[pos++].id = j;

    idx = rotate(P[i], rad).x;

    sort(tmp, tmp + pos, cmp);

    return tmp[0].id;
}


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

    for(int i=0; i<n; i++)
    {
        cnt = 0;
        divi[cnt++] = 0;
        divi[cnt++] = pi * 2;
        for(int j=0; j<n; j++)
            if(i != j)
                for(int k=j + 1; k<n; k++)
                    if(k != i)
                    {
                        if(sign(cross(P[j] - P[i], P[k] - P[i]))== 0) continue;//不要也可以ac
                        add(P[k] - P[j]);
                        add((P[k] + P[j]) / 2 - P[i]);
                    }
        sort(divi, divi + cnt, less<double> ());

        for(int j=1; j<cnt; j++){
            double mid = (divi[j] + divi[j - 1]) / 2;
            ans[i][find(mid, i)] += (divi[j] - divi[j - 1]);
        }
    }

    for(int i=0; i<n; i++)
    {
        for(int j=0; j<n; j++)
            printf("%.11f ", ans[i][j] / pi / 2);
        printf("\n");
    }

    system("pause");
    return 0;
}

河南省第十三届省赛 Dance with a stick

思路:

  • 发现棍子的左右点的个数是不变的。存在解的话排序找中点即可。

AC代码:

#include<bits/stdc++.h>
using namespace std;
 
typedef long long LL;

const int N = 1e5 + 10;
pair<int,int>q[N];
 
int main()
{
    int n; scanf("%d", & n);
    for(int i=0 ; i < n; i++) scanf("%d %d",&q[i].first, &q[i].second);
    sort(q, q + n);

    if(n == 1){
        printf("Yes\n%d %d 0 1",q[0].first, q[0].second);
        // system("pause");
        return 0;
    }

    if((n & 1) == 0){
        printf("No\n");
        // system("pause");
        return 0;
    }
    int mid = (n - 1)/2;

    printf("Yes\n");
    printf("%d %d ",q[mid].first, q[mid].second);

    if(q[mid - 1].first == q[mid].first || q[mid + 1].first == q[mid].first) printf("-1 300000000");
    else printf("0 1");


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

ACM-ICPC 2018 徐州赛区网络预赛 Trace

题解:2018 icpc徐州站网络赛 G Trace

ps:不用线段树就行,set维护即可。

AC代码:

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

typedef long long LL;
const int N=5e4+10;

LL x[N], y[N];
int n;

LL get_ans(LL * x)
{
    LL res = 0;
    set<LL>st;
    st.insert(0);

    for(int i=n-1; i>=0; i--)
    {
        auto it = st.lower_bound(x[i]);
        it--;
        res += x[i] - *(it);
        st.insert(x[i]);
    }
    return res;
}

int main()
{
    scanf("%d",&n);

    for(int i=0; i<n; i++) scanf("%lld %lld", &x[i], &y[i]);

    LL ans = get_ans(x) + get_ans(y);

    printf("%lld", ans);

    system("pause");
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值