题目链接:http://codeforces.com/contest/1195/problem/F
题意:现在有 n n n个凸包,标号分别为 1 − n 1-n 1−n给出所有的顶点坐标,有 q q q次询问,每次询问给你一段区间,你要将区间内的凸包进行闵可夫斯基求和,然后输出求和之后凸包的顶点数量。
解题心得:就用了一个闵可夫斯基求和的定理, n n n个方向不同的向量形成的凸包有 n n n个顶点,这里要注意当模长不同方向相同时形成的是一个边,三点同线,这里不要重复计算。知道了这个之后就只需要模拟一下离线处理,用个树状数组维护一下就行了。
#include
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;
}