HDU 5032 Always Cook Mushroom(极角排序, 树状数组)

Always Cook Mushroom

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)
Total Submission(s): 467    Accepted Submission(s): 146


Problem Description
Matt has a company, Always Cook Mushroom (ACM), which produces high-quality mushrooms. 

ACM has a large field to grow their mushrooms. The field can be considered as a 1000 * 1000 grid where mushrooms are grown in grid points numbered from (1, 1) to (1000, 1000). Because of humidity and sunshine, the productions in different grid points are not the same. Further, the production in the grid points (x, y) is (x + A)(y + B) where A, B are two constant. 

Matt,the owner of ACM has some queries where he wants to know the sum of the productions in a given scope(include the mushroom growing on the boundary). In each query, the scope Matt asks is a right angled triangle whose apexes are (0, 0), (p, 0), (p, q) 1<=p, q<=1000. 

As the employee of ACM, can you answer Matt’s queries?
 

Input
The first line contains one integer T, indicating the number of test cases.

For each test case, the first line contains two integers:A, B(0<=A, B<=1000).

The second line contains one integer M(1<=M<=10^5), denoting the number of queries.

In the following M lines, the i-th line contains three integers a, b, x (1<=a, b<=10^6, 1<=x<=1000), denoting one apex of the given right angled triangle is (x, 0) and the slope of its base is (a, b). It is guaranteed that the gird points in the given right angled triangle are all in valid area, numbered from (1, 1) to (1000, 1000).
 

Output
For each test case, output M + 1 lines.

The first line contains "Case #x:", where x is the case number (starting from 1) 

In the following M lines, the i-th line contains one integer, denoting the answer of the i-th query.
 

Sample Input
  
  
2 0 0 3 3 5 8 2 4 7 1 2 3 1 2 3 3 5 8 2 4 7 1 2 3
 

Sample Output
  
  
Case #1: 1842 1708 86 Case #2: 2901 2688 200
 

Source
 


题目大意:

有1000*1000个点,点(x, y)的权值为(x+A)(y+B),对于每次询问,输入(a, b) 和 x,输出以(0, 0)、(x, 0)、(x, x/a*b) 构成的三角行(含边界)包围的点的权值和。


解题思路:

解题的关键在于抓住每个三角形都含有原点(0, 0)这一信息,又可以发现不同的三角行之间有很多重复部分,想办法减少重复计算。

1. 我们可以预处理出1000*1000个点的逆时针极角序集合。

2. 将所有的询问离线操作,按(a, b)逆时针排序,从斜率最小的开始。

3. 对于询问(ai, bi), xi,要求的权值和就是过(0, 0)斜率为(a, b)的这条线以下部分。

4. 线性扫描预处理后的点集,将斜率小于等于(a, b)的点存入树状数组中。处理完后,此次询问的权值和即为[0, xi]的前缀和。

更为形象的说,每次就是将过(0, 0)斜率为(ai, bi)的直线之下,斜率为(ai-1, bi-1)的直线之上的部分压平到x轴上,[0, xi]的前缀和即为权值和。前缀和用树状数组维护。排序是为了重复利用已经插入树状数组的部分,提高效率。


参考代码:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
using namespace std;

const int MAXN = 1010;
const int MAXM = 100010;
int nCase, cCase, A, B, M;
long long bit[MAXN], ans[MAXM];

struct Point {
    int x, y;
    Point() {}
    Point(int _x, int _y) : x(_x), y(_y) {}
    bool operator < (const Point &t) const {
        return x * t.y - t.x * y > 0;
    }
};
vector<Point> point;

struct Query {
    int id, a, b, x;
    Query() {}
    Query(int _id, int _a, int _b, int _x) : id(_id), a(_a), b(_b), x(_x) {}
    bool operator < (const Query &t) const {
        return a * t.b - t.a * b > 0;
    }
};
vector<Query> query;

void init() {
    point.clear();
    for (int i = 1; i <= 1000; i++) {
        for (int j = 1; j <= 1000; j++) {
            point.push_back(Point(i, j));
        }
    }
    sort(point.begin(), point.end());
}

void init_case() {
    memset(bit, 0, sizeof(bit));
    query.clear();
}

void input() {
    scanf("%d%d", &A, &B);
    scanf("%d", &M);
    for (int i = 0; i < M; i++) {
        int a, b, x;
        scanf("%d%d%d", &a, &b, &x);
        query.push_back(Query(i, a, b, x));
    }
    sort(query.begin(), query.end());
}

long long sum(int i) {
    long long s = 0;
    while (i > 0) {
        s += bit[i];
        i -= i & (-i);
    }
    return s;
}

void add(int i, int x) {
    while (i < MAXN) {
        bit[i] += x;
        i += i & (-i);
    }
}

void solve() {
    int idx = 0;
    for (int i = 0; i < M; i++) {
        while (point[idx].x*query[i].b - query[i].a*point[idx].y >= 0) {
            add(point[idx].x, (point[idx].x+A) * (point[idx].y+B));
            idx++;
        }
        ans[query[i].id] = sum(query[i].x);
    }
}

void output() {
    printf("Case #%d:\n", ++cCase);
    for (int i = 0; i < M; i++) {
        printf("%I64d\n", ans[i]);
    }
}

int main() {
    init();
    scanf("%d", &nCase);
    while (nCase--) {
        init_case();
        input();
        solve();
        output();
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值