Codeforces Round #574 (Div. 2) F. Geometers Anonymous Club (闵可夫斯基求和)

题目链接:http://codeforces.com/contest/1195/problem/F

题意:现在有 n n n个凸包,标号分别为 1 − n 1-n 1n给出所有的顶点坐标,有 q q q次询问,每次询问给你一段区间,你要将区间内的凸包进行闵可夫斯基求和,然后输出求和之后凸包的顶点数量。

解题心得:就用了一个闵可夫斯基求和的定理, n n n个方向不同的向量形成的凸包有 n n n个顶点,这里要注意当模长不同方向相同时形成的是一个边,三点同线,这里不要重复计算。知道了这个之后就只需要模拟一下离线处理,用个树状数组维护一下就行了。



#include <bits/stdc++.h>
using namespace std;
const int maxn = 5e5+100;
typedef pair<int,int> pii;
const int INF = 0x3f3f3f3f;

int n, q, l[maxn], r[maxn], sum[maxn], ans[maxn];//sum用于树状数组求和
map <pii, int> maps;
vector <pii> ve, temp, query;//ve记录输入点的顺序,queyry记录询问
vector <int> odr[maxn];//odr记录每次询问相同区间右端点的询问时刻

pii get_dir(int x, int y) {
    int gcd = __gcd(abs(x), abs(y));
    if(gcd) {
        x/=gcd;
        y/=gcd;
    }
    return make_pair(x, y);
}

int lowbit(int x) {
    return x & -x;
}

void add(int pos, int va) {
    while(pos < maxn) {
        sum[pos] += va;
        pos += lowbit(pos);
    }
}

int get_sum(int pos) {
    if(pos <= 0) return 0;
    int Sum = 0;
    while(pos > 0) {
        Sum += sum[pos];
        pos -= lowbit(pos);
    }
    return Sum;
}

void init() {
    scanf("%d", &n);
    ve.push_back(make_pair(-1, -1));
    for(int i=1;i<=n;i++) {
        int m; scanf("%d", &m);
        for(int j=1;j<=m;j++) {
            int a, b; scanf("%d%d", &a, &b);
            temp.push_back(make_pair(a, b));
        }
        l[i] = ve.size();
        for(int j=0;j<temp.size();j++) {
            ve.push_back(get_dir(temp[j].first - temp[(j+1)%temp.size()].first, temp[j].second - temp[(j+1)%temp.size()].second));
        }
        temp.clear();
        r[i] = ve.size()-1;
    }
}

int main() {
    //    freopen("1.in.txt" , "r", stdin);
    init();

    scanf("%d", &q);
    for(int i=1;i<=q;i++) {
        int a, b;
        scanf("%d%d", &a, &b);
        pii temp;
        temp.first = l[a];
        temp.second = r[b];
        query.push_back(temp);
        odr[temp.second].push_back(i-1);
    }

    for(int i=1;i<ve.size();i++) {
        if(maps.count(ve[i])) {
            add(maps[ve[i]], -1);
        }
        add(i, 1);
        maps[ve[i]] = i;

        for(int j=0;j<odr[i].size();j++)
            ans[odr[i][j]]= get_sum(query[odr[i][j]].second) - get_sum(query[odr[i][j]].first-1);
    }
    for(int i=0;i<q;i++) {
        printf("%d\n", ans[i]);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值