NOI模拟(5.19) JSOID2T1 部落战争 (bzoj5317)

部落战争

题目背景:

5.19 模拟 JSOI2018D2T1

分析:计算几何 + 凸包

 

先得到两个图形的凸包,考虑对于第二个凸包上的一个点的坐标(x, y),如果在移动某个向量后与第一个凸包相交,考虑移动向量满足的条件,就应该是将一凸包移动向量(-x, -y)所得到的位置,这个移动后的凸包中的所有位置都是不可行的,这样对于二凸包上的每一个点,我们都可以找到对应的不可行的凸包,然后这些所有不可行位置就是二凸包坐标翻转后在每个顶点上接一个一凸包,我们只要对这些接上的凸包做一遍凸包就是所有不可行区域了,我们可以发现,这个总的凸包就是将一凸包和二凸包所有的线段按照斜率排序然后拼起来所形成的,每次判断向量只需要二分一下是否在凸包内就可以了。复杂度O((n + q)logn)

 

Source:


/*
    created by scarlyw
*/
#include <cstdio>
#include <string>
#include <algorithm>
#include <cstring>
#include <iostream>
#include <cmath>
#include <cctype>
#include <vector>
#include <set>
#include <queue>
#include <ctime>
#include <bitset>
 
inline char read() {
    static const int IN_LEN = 1024 * 1024;
    static char buf[IN_LEN], *s, *t;
    if (s == t) {
        t = (s = buf) + fread(buf, 1, IN_LEN, stdin);
        if (s == t) return -1;
    }
    return *s++;
}
 
///*
template<class T>
inline void R(T &x) {
    static char c;
    static bool iosig;
    for (c = read(), iosig = false; !isdigit(c); c = read()) {
        if (c == -1) return ;
        if (c == '-') iosig = true; 
    }
    for (x = 0; isdigit(c); c = read()) 
        x = ((x << 2) + x << 1) + (c ^ '0');
    if (iosig) x = -x;
}
//*/

const int OUT_LEN = 1024 * 1024;
char obuf[OUT_LEN];
char *oh = obuf;
inline void write_char(char c) {
	if (oh == obuf + OUT_LEN) fwrite(obuf, 1, OUT_LEN, stdout), oh = obuf;
	*oh++ = c;
}


template<class T>
inline void W(T x) {
	static int buf[30], cnt;
	if (x == 0) write_char('0');
	else {
		if (x < 0) write_char('-'), x = -x;
		for (cnt = 0; x; x /= 10) buf[++cnt] = x % 10 + 48;
		while (cnt) write_char(buf[cnt--]);
	}
}

inline void flush() {
	fwrite(obuf, 1, oh - obuf, stdout), oh = obuf;
}
 
/*
template<class T>
inline void R(T &x) {
    static char c;
    static bool iosig;
    for (c = getchar(), iosig = false; !isdigit(c); c = getchar())
        if (c == '-') iosig = true; 
    for (x = 0; isdigit(c); c = getchar()) 
        x = ((x << 2) + x << 1) + (c ^ '0');
    if (iosig) x = -x;
}
//*/

inline void write(bool flag) {
    std::cout << (flag ? "1" : "0") << '\n'; 
    // (flag ? write_char('1') : write_char('0')), write_char('\n');
}

const int MAXN = 100000 + 10;

struct point {
    long long x, y;
    
    point() {}
    point(long long x, long long y) : x(x), y(y) {}

    inline point operator + (const point &a) const {
        return point(x + a.x, y + a.y);
    } 

    inline point operator - (const point &a) const {
        return point(x - a.x, y - a.y);
    }

    inline long long operator ^ (const point &a) const {
        return x * a.y - y * a.x;
    }

    inline long long dis2() const {
        return x * x + y * y;
    }
} p1[MAXN], p2[MAXN], p[MAXN << 1 | 1], cur;

inline bool comp(const point &a, const point &b) {
    long long det = (a - cur ^ b - cur);
    return (det) ? (det > 0) : (a.dis2() < b.dis2());
}

int n, m, q, x, y;

inline bool check(point a, point b, point c) {
    return (c - a ^ b - a) >= 0;
}

inline void graham(point *p, int &n) {
    for (int i = 1; i <= n; ++i)
        if (p[i].x < p[1].x || (p[i].x == p[1].x && p[i].y < p[1].y))
            std::swap(p[1], p[i]);
    std::vector<point> q;
    cur = p[1], std::sort(p + 2, p + n + 1, comp), q.push_back(p[1]);
    for (int i = 2; i <= n; ++i) {
        while (q.size() > 1 && check(q[q.size() - 2], q.back(), p[i]))  
            q.pop_back();
        q.push_back(p[i]);
    }
    n = q.size();
    for (int i = 0; i < n; ++i) p[i + 1] = q[i];
}

inline void merge(point *p1, point *p2) {
    static point v1[MAXN], v2[MAXN];
    for (int i = 1; i <= n; ++i) v1[i] = (p1[i == n ? 1 : i + 1] - p1[i]);
    for (int i = 1; i <= m; ++i) v2[i] = (p2[i == m ? 1 : i + 1] - p2[i]);
    int cnt = 1, l1 = 1, l2 = 1;
    p[1] = p1[1] + p2[1];
    while (l1 <= n && l2 <= m) 
        p[++cnt] = p[cnt - 1] + ((v1[l1] ^ v2[l2]) >= 0 ? v1[l1++] : v2[l2++]); 
    while (l1 <= n) p[++cnt] = p[cnt - 1] + v1[l1++];
    while (l2 <= m) p[++cnt] = p[cnt - 1] + v2[l2++];
}

inline bool inside(point u) {
    if ((u - p[1] ^ p[2] - p[1]) > 0 || (p[n + m] - p[1] ^ u - p[1]) > 0) 
        return false;
    int l = 1, r = n + m;
    while (l + 1 < r) {
        int mid = l + r >> 1;
        ((u - p[1] ^ p[mid] - p[1]) >= 0) ? r = mid : l = mid;
    }
    return ((p[r] - p[r - 1] ^ u - p[r - 1]) >= 0);
}

inline void solve() {
    R(n), R(m), R(q);
    for (int i = 1; i <= n; ++i) R(p1[i].x), R(p1[i].y);
    for (int i = 1; i <= m; ++i) 
        R(p2[i].x), R(p2[i].y), p2[i].x = -p2[i].x, p2[i].y = -p2[i].y;
    graham(p1, n), graham(p2, m), merge(p1, p2);
    while (q--) R(x), R(y), write(inside(point(x, y)));
}

int main() {
    // freopen("in.in", "r", stdin);
    solve();
    // flush();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值