• 「6月雅礼集训 2017 Day5」仰望星空


    【题目大意】

    给你$n$个点,被一个半径为$R$的元圆划分成内(包含边界)、外两个部分。

    要连若干线,每个点只能连一条线,不存在重点和三点共线。

    线只能连在内部点和外部点之间,线长度不超过$d$。

    如果一个外部点$w$和三个内部点$x,y,z$距离都不超过$d$,且两个内部点$x,z$没有和外部点$w$连线,我们准备从外部点$w$连到内部点$y$。那么如果$x$和$z$的连线有和$w$和$y$的连线相交,那么是不合法的。

    求合法情况下,最多连多少线。以及方案。

    $1 leq n leq 10^3, 1 leq |x_i|, |y_i|, d, R leq 2 imes 10^4$

    【题解】

    考虑先把点分类。很明显发现答案=最大匹配。

    对于每个外部点,以它为中心把内部点极角排序,那么得到了一个访问序列,然后我们直接跑匈牙利即可。

    考虑这样如何保证那个奇怪的条件:

    如果$x,y,z$按极角序顺次排过来,那么扫到$y$的时候,$x$如果没被匹配,一定先被扫描了,并当做匹配点。

    所以这样保证了没有奇怪的条件这个情况。

    然后我们就能求出匹配,考虑求方案。

    对于匹配,需要找到一个合法的连边顺序(就保证一定要按这个极角序从前往后连边即可)

    暴力找方案即可。

    总复杂度$O(n^3)$,匈牙利跑不满,而且n一般来说为n/2(因为把点分成了一半)

    这里极角排序可以用叉积,因为是圆外的点朝圆内的点连边,角度范围小于180°。

    # include <stdio.h>
    # include <string.h>
    # include <iostream>
    # include <algorithm>
    
    using namespace std;
    
    typedef long long ll;
    typedef unsigned long long ull;
    typedef long double ld;
    
    const int M = 1010, N = 1e5 + 10;
    const int mod = 998244353;
    
    int n, R, d;
    struct P {
        int x, y;
        P() {}
        P(int x, int y) : x(x), y(y) {}
        friend P operator + (P a, P b) {
            return P(a.x + b.x, a.y + b.y);
        }
        friend P operator - (P a, P b) {
            return P(a.x - b.x, a.y - b.y);
        }
        friend int operator * (P a, P b) {
            return a.x * b.y - a.y * b.x;
        }
    }a[M], b[M], C;
    int an, bn;
    
    struct pa {
        P a;
        int id;
        pa() {}
        pa(P a, int id) : a(a), id(id) {}
    }c[M]; int cn;
    
    P cmp_t;
    int g[M][M], gn[M];
    inline bool cmp(pa a, pa b) {
        return (a.a - cmp_t) * (b.a - cmp_t) > 0;
    }
    
    inline ll dis2(P a, P b) {
        return (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y);
    }
    
    
    bool vis[M];
    int from[M], to[M];
    int aid[M], bid[M];
    
    inline int hungry(int x) {
        for (int i=1; i<=gn[x]; ++i) {
            if(!vis[g[x][i]]) {
                vis[g[x][i]] = 1;
                if(from[g[x][i]] == 0 || hungry(from[g[x][i]])) {
                    from[g[x][i]] = x;
                    return 1;
                }
            }
        }
        return 0;
    }
    
    int main() {
        freopen("etoile.in", "r", stdin);
        freopen("etoile.out", "w", stdout);
        cin >> n >> R >> d;
        for (int i=1, _x, _y; i<=n; ++i) {
            scanf("%d%d", &_x, &_y);
            if(i == 1) C = P(_x, _y);
            if(dis2(P(_x, _y), C) <= (ll)R * R) aid[++an] = i, a[an] = P(_x, _y);
            else bid[++bn] = i, b[bn] = P(_x, _y);
        }
        
    //    puts("===========");
    //    for (int i=1; i<=an; ++i) printf("%d %d
    ", a[i].x, a[i].y);
    //    puts("===========");
    //    for (int i=1; i<=bn; ++i) printf("%d %d
    ", b[i].x, b[i].y);
    //    puts("===========");
        
        for (int i=1; i<=bn; ++i) {
            cmp_t = b[i]; cn = 0;
            for (int j=1; j<=an; ++j) 
                if(dis2(a[j], cmp_t) <= (ll)d * d) c[++cn] = pa(a[j], j);
            sort(c+1, c+cn+1, cmp);
            for (int j=1; j<=cn; ++j) g[i][j] = c[j].id;
            gn[i] = cn;
        }
        
        int ans = 0;
        for (int i=1; i<=bn; ++i) {
            memset(vis, 0, sizeof vis);
            ans += hungry(i);
        }
    
        for (int i=1; i<=an; ++i) to[from[i]] = i;
    
        memset(vis, 0, sizeof vis);
        cout << (ans << 1) << endl;
        
        for (int i=1; i<=ans; ++i) {
            bool hv = 0;
            for (int j=1; j<=bn; ++j) {
                if(!to[j]) continue;
                for (int k=1; k<=gn[j]; ++k) {
                    if(!vis[g[j][k]]) {
                        if(g[j][k] == to[j]) hv = 1;
                        break;
                    }
                }
                if(hv) {
                    vis[to[j]] = 1;
                    printf("%d %d
    ", bid[j], aid[to[j]]);
                    break;
                }
            }
        }
        return 0;
    }
    /*
    10 5530 5385
    8 5730
    5220 61
    2896 2950
    1025 649
    5509 1773
    6057 2432
    6435 975
    5366 8341
    1127 3616
    2849 1689
    */
    View Code
  • 相关阅读:
    组合
    面向对象初识, 类名,对象的的使用, 类名称空间和对象名称空间
    内置函数2 递归函数
    内置函数~~~
    生成器 列表推导式 列表表达式
    函数名应用 闭包 迭代器
    函数 动态参数, 名称空间 作用域 取值顺序,函数的嵌套
    函数初识~~
    文件操作要点
    Mysql索引原理
  • 原文地址:https://www.cnblogs.com/galaxies/p/20170621_b.html
Copyright © 2020-2023  润新知