几何入门合集 gym101968 problem F. Mirror + gym102082 Problem F Fair Chocolate-Cutting + gym101915 problem ...

abstract:

  1. V const & a 加速

F. Mirror

题意

链接

问题:
有n个人在y=0的平面上(及xoz平面)。z=0平面上有一面镜子(边平行于坐标轴)。z=a平面上有q个点(保证a大于所有人的z坐标)。 所有人面朝镜子,且在镜子和q个点之间(即每个人的z坐标保证0 问对于某个点,让所有人能够通过镜子看到那个点的镜子的最小面积。


题解

三维几何+镜像+凸包
首先考虑镜面,我们可以通过(初中科学的)镜面反射原理,关于z=0做出z=a的对称平面z=-a。问题就变成了n个人看z=-a上的某个点。(下图绿点是人,红点是询问点)

几何入门合集 gym101968 problem F. Mirror + gym102082 Problem F Fair Chocolate-Cutting + gym101915 problem ..._第1张图片

然后观察,镜子的高和宽是独立的。 于是我们分别求它们的最大值即可。
求高比较简单,我们朝-x方向看yoz平面。通过把每个人跟点Q的像Q‘相连,我们发现离Q’z轴距离最近的人对应着镜子的下边界,最远的人对应着上边界,通过维护所有人z坐标的max_z&min_z以及相似三角形可以直接求出两个边界,复杂度为O(1)。
我们用同样的方法,朝-y方向看xoz平面。 通过把每个人跟点Q的像Q‘相连,我们发现,左右边界并不对应着最左边与最右边的人。而且随着询问点的变化,对应着左右边界的人也在变化。
如果我们暴力的找对应左右边界的人,复杂度为n*q ,不可行。
我们发现,对应着左右边界的人虽然随着询问点变化,但他们都在凸包上(如下图)。

几何入门合集 gym101968 problem F. Mirror + gym102082 Problem F Fair Chocolate-Cutting + gym101915 problem ..._第2张图片

更进一步,如果我们将询问点按照x坐标排序,随着询问的x坐标增加,左右边界的人在凸包上的变化是顺时针旋转的。(考虑你从左到右观察一个正前方的凸包)
于是我们就能够通过一个nlogn的凸包预处理然后O(1)地回答每个询问,复杂度为O(q+nlogn)

剩下的是实现”从左到右看凸包时凸包左右边界的顺时针更新“。
首先是写out,in函数(右手法则,向外转就是逆时针),用来逆时针、顺时针遍历凸包上的点。因为极角排序凸包存的点是逆时针的(极角排序的那个角是与y轴的逆时针夹角。),所以out就是++。
先找到凸包的下上顶点,作为初始的左右边界的对应点。
然后根据x坐标从小到大枚举询问点Q。
对于每个Q,不断顺时针更新左边界对应的人,直到他与Q的连线在他凸包上顺时针的下一个人与Q的连线的外面(直观上显然正确)。 右边界同理。
某些编辑器比如codeforces不能混用iostream与stdio


代码

#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define debug(x) cerr<<#x<<" = "<<(x)< Q[maxn];
bool cmpr(V const &a, V const &b) {
    V v1 = a - st[0], v2 = b - st[0];
    return (v1 | v2) < -eps;
}

vector ch;
void getCH() {
    sort(st + 1, st + n, cmpr);
    ch.push_back(st[0]);
    rep(i, 1, n-1) {
        while (ch.size() > 1 && (st[i] - ch.back() | ch.back() - ch[ch.size() - 2]) < eps)ch.pop_back();
        ch.push_back(st[i]);
    }
}
int out(int x) { return x ? x - 1 : ch.size() - 1; }
int in(int x) { return x + 1 == (int)ch.size() ? 0 : x + 1; }


double getx(double xs, double z, double xq) {
    return xs + z / (z + a) * (xq - xs);
}



int main() {
    //FAST_IO;
    int t;
    cin >> t;
    while (t--) {

        cin >> n >> a;
        
        rep(i, 0, n - 1)st[i].sc();
        db zmn = st[0].y, zmx = st[0].y;
        rep(i, 0, n - 1) {
            zmn = min(zmn, st[i].y);
            zmx = max(zmx, st[i].y);
        
        }
        rep(i, 0, n - 1)st[i].y = a - st[i].y;
        sort(st, st + n);
        ch.clear();
        getCH();

        cin >> q;
        ll qx = 0, qy = 0;
        rep(i, 1, q) {
            Q[i].first.sc();
            Q[i].second = i;
        }
        sort(Q + 1, Q + 1 + q);

        int lp = 0, rp = 0;
        rep(i, 0, ch.size() - 1)ch[i].y = a - ch[i].y;
        while (ch[in(rp)].y < ch[rp].y)rp = in(rp);
        while (ch[out(lp)].y > ch[lp].y)lp = out(lp);

        rep(i, 1, q) {
            while (true) {
                int ni = in(lp);
                if (getx(ch[ni].x, ch[ni].y, Q[i].first.x) < getx(ch[lp].x, ch[lp].y, Q[i].first.x))lp = ni;
                else break;
            }
            while (true) {
                int ni = in(rp);
                if (getx(ch[ni].x, ch[ni].y, Q[i].first.x) > getx(ch[rp].x, ch[rp].y, Q[i].first.x))rp = ni;
                else break;
            }
            double x = abs(getx(ch[rp].x, ch[rp].y, Q[i].first.x) - getx(ch[lp].x, ch[lp].y, Q[i].first.x));
            double h = getx(0, zmx, Q[i].first.y) - getx(0, zmn, Q[i].first.y);
            ans[Q[i].second] = (long double)x * h;
            //printf("%.20lf\n", x*h);
        }
        rep(i, 1, q)printf("%.20lf\n", (double)ans[i]);
    }
    cin >> n;
}

/*
1
3 3
-2 1
7 2
3 1
3
2 5
-2 4
8 10
*/


心路历程

当有两个以上的bug时你就炸了
wa0:FAST_IO codeforces以用就wa

wa1:输出rep(i,1,q) not rep(i,1,n)//最后才发现
wa2:凸包板子里面下标从0开始。

Problem F Fair Chocolate-Cutting

题意

来源:ICPC Asia Regional Contest, Yokohama, 2018–12–09

让你求所有平分凸多边形的线段中最长和最短的那两条。
输出它们的长度。
---

题解

证明题+硬核平面几何
我们有结论:(目前不会证明)
某合法(平分多边形的)线段的端点在顶点上时一定是局部最长或最短的。
除此之外,当某合法线段垂直于任意“外角”平分线时,也是局部最短的

于是问题就转化成
1.对于每个顶点,找到过它的合法线段。
几何入门合集 gym101968 problem F. Mirror + gym102082 Problem F Fair Chocolate-Cutting + gym101915 problem ..._第3张图片

2.对于每个角(跳过一些线段的“相邻”两条线段的延长线交出的角),找到垂直它的平分线的合法线段。注意特判掉角不存在的情况,即两边平行。

几何入门合集 gym101968 problem F. Mirror + gym102082 Problem F Fair Chocolate-Cutting + gym101915 problem ..._第4张图片

对于1,只要从该顶点开始不断逆时针扫顶点,直到刚刚超过1/2的总面积。然后把上一个三角形分割一下。
对于2,直接用平面几何知识以及三角函数求解
---

代码

#include
#include 
#include 
#include 
#include 
#include 
#include
#include
typedef long long ll;
typedef double db;
typedef long double ld;

using namespace std;
typedef complex  point;
#define rep(i,j,k) for(int i = (int)j;i <= (int)k;i ++)
#define F(i,N)for(int i = 0;i < (int)(N);i ++)
#define Decimal fixed< V;
double sumS,nowS,mx,mn;
double cross(const point& a, const point& b) {
    return imag(conj(a)*b);
}
double dot(const point& a, const point& b) {
    return real(conj(a)*b);
}
double area(const vector& p) {
    double A = 0;
    F(i, p.size()) A += cross(curr(p, i), next(p, i));
    return A / 2.;
}
double triArea(int a, int b, int c) {
    a %= n, b %= n, c %= n;
    if (a == b || b == c || c == a)return 0;
    vector tmp = { V[a],V[b],V[c] };
    return area(tmp);
}
double distancePP(point const &a, point const &b) {
    return abs(a - b);
}
//seg
struct segment :public vector {
    segment(const point &a, const point &b) {
        push_back(a); push_back(b);
    }
};
point crosspoint(segment const &l,segment const &m) {
    double A = cross(l[1] - l[0], m[1] - m[0]);
    double B = cross(l[1] - l[0], l[1] - m[0]);
    if (abs(A) < eps&&abs(B) < eps)return m[0];//same line
    if (abs(A) < eps)return point(INF, INF);//parallel
    return m[0] + B / A*(m[1] - m[0]);
}
double angle(const point& a, const point& b) {
    auto tmp = abs(arg(a) - arg(b));
    return min(tmp, 2 * PI - tmp);
}
double angle(const segment &s1, const segment &s2) {
    return angle(s1[1] - s1[0], s2[1] - s2[0]);
}
point projection(const segment &l, const point &p) {
    double t = dot(p - l[0], l[0] - l[1]) / norm(l[0] - l[1]);
    return l[0] + t * (l[0] - l[1]);
}
double distanceLP(const segment &l, const point &p) {
    return abs(p - projection(l, p));
}
bool intersectSP(const segment &s,const point &p) {//point on seg
    return abs(s[0] - p) + abs(s[1] - p) - abs(s[1] - s[0]) < eps;
}
double seica(int a,int b) {
    a %= n; b %= n;
    vector tmp;
    int i = a;
    while (1) {
        tmp.EB(V[i]);
        if (i == b)break;
        i = (i + 1) % n;
    }
    if (tmp.size() < 3)return 0;
    else return area(tmp);
}
void checkV(int p,int q) {
    p %= n; q %= n;
    double remain = (sumS / 2) - nowS;
    double rate = remain / triArea(p, q, q + 1);
    point tmp = V[q] + rate * (V[(q + 1) % n] - V[q]);
    mn = min(mn, distancePP(V[p], tmp));
    mx = max(mx, distancePP(V[p], tmp));
}
void checkSeg(int p, int q) {
    p %= n, q %= n;
    nowS -= triArea(p, p + 1, q);
    segment a(V[(p + 1) % n], V[p]);
    segment b(V[(q) % n], V[(q + 1) % n]);
    point crossp = crosspoint(a, b); 
    if (crossp == point(INF, INF)) {
        double sqS = area({ V[p],V[(p + 1) % n],V[q],V[(q + 1) % n] });
        if (nowS + sqS < sumS / 2 + eps)return;
        bool flag = 0;
        if (intersectSP(b, projection(b, a[0])) || intersectSP(b, projection(b, a[1])))flag = 1;
        if (intersectSP(a, projection(a, b[0])) || intersectSP(a, projection(a, b[1])))flag = 1;
        if (!flag)return;
        mn = min(mn, distanceLP(b, a[0]));
        return;
    }
    double tmpS=0;
    if (distancePP(crossp, a[0]) > distancePP(crossp, a[1]) + eps) {
        tmpS = seica(q + 1, p);
        swap(a[0], a[1]);
        swap(b[0], b[1]);
    }
    else {
        tmpS= seica(p + 1, q);
    }
    double theta = angle(a, b);
    double T = distancePP(crossp, a[0])*distancePP(crossp, b[0])*sin(theta) / 2;
    double leg = (2 * (T - tmpS) + sumS) / sin(theta);
    bool flag = 0;
    if (leg > distancePP(a[1], crossp)*distancePP(a[1], crossp) + eps)flag = 1;
    if (leg + eps < distancePP(a[0], crossp)*distancePP(a[0], crossp))flag = 1;
    if (leg > distancePP(b[1], crossp)*distancePP(b[1], crossp) + eps)flag = 1;
    if (leg + eps < distancePP(b[0], crossp)*distancePP(b[0], crossp))flag = 1;
    if (flag)return;
    mn = min(mn, 2*sqrt(leg)*sin(theta / 2));
}
int main() {
    mx = 0;
    mn = 1e18;
    cin >> n;
    F(i, n) {
        db a, b;
        cin >> a >> b;
        V.EB(a, b);
    }
    int q = 1;
    sumS = area(V);
    F(i, n) {
        while (nowS + triArea(i, q, q + 1) < sumS / 2 + eps) {
            nowS += triArea(i, q, q + 1);
            q++;
        }
        checkV(i,q);
        checkSeg(i,q);
    }
    cout << Decimal << mn << endl;
    cout << Decimal << mx << endl;
    cin >> n;
    return 0;
}






/*
4
0 0
10 0
10 10
0 10


3
0 0
6 0
3 10
*/


心路历程


关键是不会证明。  
复数大法好啊!!!(其实还是慢5倍??下面那题)


B. Ali and Wi-Fi

题意

来源ACM International Collegiate Programming Contest, Tishreen Collegiate Programming Contest
100个圆,每个圆代表wifi覆盖区域,每个wifi有一个网速。你最多可以连入m个WiFi,网速为这些wifi的总和。
问你最大的网速是多少。


题解

想法题+圆模板
我们知道n个圆最多可以把平面分成n^2-n+2个区域(递推进阶题233)。所以可以考虑遍历每个区域,计算它被哪些圆。。。
我们发现维护一块区域很难,而要取最大的m个更难考虑。
于是考虑维护圆与圆的交点。我们发现每个区域都是圆交出来的,i.e.交点都在区域的边界上。
于是我们可以枚举交点来代替枚举区域,显然交点个数也是n^2级别的。(考虑圆内含的情况,所以要把每个圆心也枚举一遍)
这样枚举的另一个好处是可以很简单地处理m:
枚举每个交点,计算有几个圆包含它。然后直接在这些圆中取最大的m个。
复杂度为n^2nnlogn。
如果我们用优先队列来维护最大的m个,那么复杂度里可以去掉一个n。


代码

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
#define debug(x) cerr<<#x<<" = "<<(x)< point;
#define X real()
#define Y imag()
const int maxn = 2e5 + 5;
const db eps = 1e-10;
const db PI = acos(-1.);
int dcmp(ld x)
{
    if (fabs(x) < eps) return 0;
    return x < 0 ? -1 : 1;
}
struct V {
    ld x, y;
    V() {}
    void sc() { scanf("%lf%lf", &x, &y); }
    V(double a, double b) : x(a), y(b) { }
    V operator+(const V &o)const { return V(x + o.x, y + o.y); }
    V operator-(const V &o)const { return V(x - o.x, y - o.y); }
    double L() { return sqrt(x * x + y * y); }
    V N() {
        double l = L();
        return V(x / l, y / l);
    }
    V rot(double th) { return V(x * cos(th) - y * sin(th), x * sin(th) + y * cos(th)); }
    V operator*(double z) { return V(x * z, y * z); }
    double operator*(const V &o) const { return x * o.x + y * o.y; }
    double operator|(const V &o) const { return x * o.y - o.x * y; }
    double operator==(const V &o) const { return dcmp(x - o.x) == 0 && dcmp(y - o.y) == 0; }
    void pr() { printf("%lf %lf\n", x, y); }
};
vector p;
typedef V point;
V conj(V p) { return V(p.x, -p.y); }
double arg(V p) { return atan2(p.y, p.x); }
double norm(V p) { return p.L()*p.L(); }
double abs(V p) { return p.L(); }



double cross(const point& a, const point& b) {
    return imag(conj(a)*b);
}
double dot(const point& a, const point& b) {
    return real(conj(a)*b);
}
struct circle
{
    point c;
    ld r, v;
    circle() {}
    circle(point c, ld r, ld v) :c(c), r(r), v(v) {}

    inline point pt(double a)
    {
        return point(c.x + cos(a)*r, c.y + sin(a)*r);
    }
};
int getCircleCircleIntersection(circle C1, circle C2, point &t1, point &t2)
{
    ld d = abs(C1.c - C2.c);
    if (dcmp(d) == 0)
    {
        if (dcmp(C1.r - C2.r) == 0) return -1;// same cir
        return 0;// include
    }
    if (dcmp(C1.r + C2.r - d) < 0) return 0;// disjoint
    if (dcmp(fabs(C1.r - C2.r) - d) > 0) return 0;// include
    ld a = arg(C2.c - C1.c);
    ld da = acos((C1.r*C1.r + d * d - C2.r*C2.r) / (2 * C1.r*d));
    point p1 = C1.pt(a - da), p2 = C1.pt(a + da);
    t1 = p1;
    if (p1 == p2) return 1;
    t2 = p2;
    return 2;
}
vector A;
circle aa[105];
vector P;
point pp[10105];
priority_queue Q;
int  ans, sum;
int n, m, tot;
int main() {
    FAST_IO;
    int t;

    cin >> t;
    while (t--) {

        tot = ans = 0;
        cin >> n >> m;
        F(i, n) {

            cin >> aa[i].c.x >> aa[i].c.y >> aa[i].r >> aa[i].v;

        }
        F(i, n) {
            rep(j, i + 1, n - 1) {
                point t1, t2;
                int num = getCircleCircleIntersection(aa[i], aa[j], t1, t2);
                if (num == 1) {
                    pp[tot++] = t1;
                }
                else if (num == 2) {
                    pp[tot++] = t1;
                    pp[tot++] = t2;
                }
            }
        }
        F(i, n)pp[tot++] = aa[i].c;
        F(j, tot) {
            F(i, n) {
                if (dcmp(abs(pp[j] - aa[i].c) - aa[i].r) <= 0) {
                    Q.push(-aa[i].v);
                    if (Q.size() > m)Q.pop();
                }
            }
            sum = 0;
            while (!Q.empty()) {
                sum -= Q.top();
                Q.pop();
            }
            ans = max(ans, sum);
        }
        cout << ans << endl;
    }
    cin >> t;
    return 0;
}


心路历程

没初始化。
瞎debug发现long double int求和会爆精度。
priority_Q太久没用,忘记pop是从头上弹出的了orz。
vector 和对于1e6的数据其实只比数组慢10ms。

转载于:https://www.cnblogs.com/SuuT/p/10669811.html

你可能感兴趣的:(几何入门合集 gym101968 problem F. Mirror + gym102082 Problem F Fair Chocolate-Cutting + gym101915 problem ...)