[BZOJ2850]巧克力王国(KD-tree)

题目:

我是超链接

题解:

和上一道题目一样,这道题目的基本思路:抽象成二维—>每次查询从树里找有效解,分区间查询
我们可以把x,y抽象为二维的点,那么每次询问都是ax+by<=c就是这条直线的下方点权数
难点就是如何判断这条直线有没有与某个区间相交,我们已经用KD-tree记录了4个值:管辖区间内的最左值、最右值、最上值、最下值
可以组合为4个极端点:左上、右上、左下、右下
如果这四个极端点都在范围内,那就是全都涵盖啦,我们就一股脑的加进去
如果这四个极端点都不在范围内,那就是没有交点啊,直接跳过

代码:

#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
#define LL long long
struct hh{int l,r,d[2],mn[2],mx[2];LL sum,zz;}t[50005];
int cmpd,root;LL ans,a,b,c;
int cmp(const hh &a,const hh &b)
{
    return (a.d[cmpd]<b.d[cmpd] || (a.d[cmpd]==b.d[cmpd] && a.d[!cmpd]<b.d[!cmpd]));
}
void updata(int now)
{
    int lc=t[now].l,rc=t[now].r;
    t[now].sum=t[now].zz;
    if (lc)
    {
        t[now].mn[0]=min(t[now].mn[0],t[lc].mn[0]);
        t[now].mn[1]=min(t[now].mn[1],t[lc].mn[1]);
        t[now].mx[0]=max(t[now].mx[0],t[lc].mx[0]);
        t[now].mx[1]=max(t[now].mx[1],t[lc].mx[1]);
        t[now].sum+=t[lc].sum;
    }
    if (rc)
    {
        t[now].mn[0]=min(t[now].mn[0],t[rc].mn[0]);
        t[now].mn[1]=min(t[now].mn[1],t[rc].mn[1]);
        t[now].mx[0]=max(t[now].mx[0],t[rc].mx[0]);
        t[now].mx[1]=max(t[now].mx[1],t[rc].mx[1]);
        t[now].sum+=t[rc].sum;
    }
}
int build(int l,int r,int D)
{
    cmpd=D;
    int mid=(l+r)>>1;
    nth_element(t+l+1,t+mid+1,t+r+1,cmp);
    t[mid].mn[0]=t[mid].mx[0]=t[mid].d[0];
    t[mid].mn[1]=t[mid].mx[1]=t[mid].d[1];
    t[mid].sum=t[mid].zz;
    if (l<mid) t[mid].l=build(l,mid-1,!D);
    if (r>mid) t[mid].r=build(mid+1,r,!D);
    updata(mid); return mid;
}
bool ok(int now){return ((LL)t[now].d[0]*a+(LL)t[now].d[1]*b<c);}
int check(int now)
{
    int d=0;
    if ((LL)t[now].mn[0]*a+(LL)t[now].mn[1]*b<c) d++;//左下
    if ((LL)t[now].mn[0]*a+(LL)t[now].mx[1]*b<c) d++;//左上
    if ((LL)t[now].mx[0]*a+(LL)t[now].mn[1]*b<c) d++;//右下
    if ((LL)t[now].mx[0]*a+(LL)t[now].mx[1]*b<c) d++;//右上
    return d;
}
void qurry(int now)
{
    if (ok(now)) ans+=t[now].zz;
    if (t[now].l)
    {
        int bh=check(t[now].l);
        if (bh==4) ans+=t[t[now].l].sum;
        else if (bh!=0) qurry(t[now].l);
    }
    if (t[now].r)
    {
        int bh=check(t[now].r);
        if (bh==4) ans+=t[t[now].r].sum;
        else if (bh!=0) qurry(t[now].r);
    }
}
int main()
{
    int n,m,i;
    scanf("%d%d",&n,&m);
    for (i=1;i<=n;i++) scanf("%lld%lld%lld",&t[i].d[0],&t[i].d[1],&t[i].zz);
    root=build(1,n,0);
    while (m--)
    {
        scanf("%lld%lld%lld",&a,&b,&c);
        ans=0;
        qurry(root);
        printf("%lld\n",ans);
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值