HDU 5032 Always Cook Mushroom 极角排序 数状数组 姿势

题意:在1000*1000的格子上,每一个格子的值为(x+A)(y+B)(A,B)为给定的值。现在要查询斜率为b/a,直角点为(x,1)的直角三角形包含的所有格子的点的和,包括边界。

思路:因为只有查询操作,我们可以将其作为离线问题。很容易想到,要对所有的斜率进行极角排序,而对于求和询问,我们可以利用数状数组。

            注意到,给定的三角形是固定一个点的,同时在x轴方向,是前缀和。所以,我们只需开一维数组即可,不用二维。

            但是如何进行更新操作呢?

            我首先想到的是:因为每个点的数值是由公式算出来的,固定X的时候,实质是一个等差数列,可以直接用公式求出。但是这样的复杂度非常高:10^5*1000*lg1000 > 10^8 好像有点高,必须优化。

            我们可以发现,对于每次更新操作,如果询问非常密集的话,有些更新为0,这些是没有必要更新的,但是怎么判断呢?

            (下面是从网上题解明白的)我们可以对这1000*1000个点进行极角排序,询问的斜率同样要进行排序。从前向后处理询问的时候,我们依次加入极角小于等于询问的点。

这样,总共的复杂度会因为平摊而大大降低。

坑:因为需要打乱排序,我们需要记录原来的编号。但是样例中给出的情况是排好序的,坑爹呀。。。

代码如下:

#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>

using namespace std;

typedef long long ll;

const int MAX = 1100;
const double EPS = 1e-6;

int dcmp(double x)
{
    if(fabs(x) < EPS)
        return 0;
    else if(x > 0)
        return 1;
    else
        return -1;
}

struct yy{
    int x,y;
    double t;
    int id;
    bool operator < (const yy & rhs) const{
        return  dcmp(t - rhs.t) < 0;
    }
} p[MAX * MAX], event[MAX*MAX];

ll sum[MAX];
ll ret[MAX*MAX];

void update(int n, ll data)
{
    for(;n <= 1000; n += n &(-n))
        sum[n]+= data;
}
ll getsum(int n)
{
    ll ans = 0;
    for(;n > 0; n -= n &(-n))
        ans += sum[n];
    return ans;
}


int main(void)
{
    //freopen("main.in","r",stdin);
    int tot = 0;
    for(int i = 1; i <= 1000; ++i){
        for(int j = 1; j <= 1000; ++j){
            p[tot].x = i, p[tot].y = j;
            p[tot++].t = (double) j / i;
        }
    }
    sort(p,p+tot);
    int T;
    scanf("%d", &T);
    for(int cas = 1; cas <= T; ++cas){
        ll A,B;
        scanf("%I64d %I64d",&A,&B);
        int M;
        scanf("%d", &M);
        for(int i = 1; i <= M; ++i){
            double a,b;
            scanf("%lf %lf %d", &a, &b,&event[i].x);
            event[i].t = b / a;
            event[i].id = i;
        }
        sort(event+1,event + M+1);
        printf("Case #%d:\n",cas);
        memset(sum,0,sizeof(sum));
        int cnt = 0;
        for(int i = 1; i <= M; ++i){
            for(;cnt < tot && dcmp(p[cnt].t - event[i].t) <= 0;cnt++){
                update(p[cnt].x,(p[cnt].x + A) * (p[cnt].y + B));
            }
            ret[event[i].id] = getsum(event[i].x);
        }
        for(int i = 1; i <= M; ++i)
            printf("%I64d\n",ret[i]);
    }
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值