[Luogu P4475] [BZOJ 2850] 巧克力王国

洛谷传送门
BZOJ传送门

题目描述

巧克力王国里的巧克力都是由牛奶和可可做成的。但是并不是每一块巧克力都受王国人民的欢迎,因为大家都不喜欢过于甜的巧克力。

对于每一块巧克力,我们设 x x y 为其牛奶和可可的含量。由于每个人对于甜的程度都有自己的评判标准,所以每个人都有两个参数 a a b ,分别为他自己为牛奶和可可定义的权重, 因此牛奶和可可含量分别为 x x y 的巧克力对于他的甜味程度即为 ax+by a x + b y 。而每个人又有一个甜味限度 c c ,所有甜味程度大于等于 c 的巧克力他都无法接受。每块巧克力都有一个美味值 h h

现在我们想知道对于每个人,他所能接受的巧克力的美味值之和为多少。

输入输出格式

输入格式:

第一行两个正整数 n m m ,分别表示巧克力个数和询问个数。
接下来 n 行,每行三个整数 x , y , h x   ,   y   ,   h ,含义如题目所示。
再接下来 m m 行,每行三个整数 a , b , c ,含义如题目所示。

输出格式:

输出 m m 行,其中第 i 行表示第 i i 个人所能接受的巧克力的美味值之和。

输入输出样例

输入样例#1:

3 3
1 2 5
3 1 4
2 2 1
2 1 6
1 3 5
1 3 7

输出样例#1:

5
0
4

说明

对于100%的数据, 1n,m50000,109ai,bi,xi,yi109

解题分析

KDTree K D − T r e e 板子题, 每个询问相当于询问一个半平面中给定的点的贡献和。

我们可以按两维交替划分的方式将区间尽量变得长宽相同。

其实真没什么好说的, 具体看注释吧…

#include <cstdio>
#include <cmath>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <cctype>
#define R register
#define IN inline
#define gc getchar()
#define W while
#define ll long long
#define MX 50500
bool neg;
template <class T>
IN void in(T &x)
{
    x = 0; R char c = gc;
    for (; !isdigit(c); c = gc)
    if(c == '-') neg = true;
    for (;  isdigit(c); c = gc)
    x = (x << 1) + (x << 3) + c - 48;
    if(neg) neg = false, x = -x;//有负数...
}
struct Node {ll sum; int son[2], lim[2][2], cood[2], val;} tree[MX];
int dot, dim, q;
ll a, b, c, root;
ll ans;
namespace KDT
{
    #define ls tree[now].son[0]
    #define rs tree[now].son[1]
    IN bool cmp(const Node &x, const Node &y)
    {return x.cood[dim] == y.cood[dim] ? x.cood[dim ^ 1] < y.cood[dim ^ 1] : x.cood[dim] < y.cood[dim];}//按划分的那一维来排序
    IN void pushup(R int now)
    {
        tree[now].sum = tree[now].val;
        if(ls)
        {//00表示x轴的区间最小值, 01为最大值, 10为y轴的区间最小值, 11为最大值
            tree[now].lim[0][0] = std::min(tree[now].lim[0][0], tree[ls].lim[0][0]);
            tree[now].lim[0][1] = std::max(tree[now].lim[0][1], tree[ls].lim[0][1]);
            tree[now].lim[1][0] = std::min(tree[now].lim[1][0], tree[ls].lim[1][0]);
            tree[now].lim[1][1] = std::max(tree[now].lim[1][1], tree[ls].lim[1][1]);
            tree[now].sum += tree[ls].sum;
        }
        if(rs)
        {
            tree[now].lim[0][0] = std::min(tree[now].lim[0][0], tree[rs].lim[0][0]);
            tree[now].lim[0][1] = std::max(tree[now].lim[0][1], tree[rs].lim[0][1]);
            tree[now].lim[1][0] = std::min(tree[now].lim[1][0], tree[rs].lim[1][0]);
            tree[now].lim[1][1] = std::max(tree[now].lim[1][1], tree[rs].lim[1][1]);
            tree[now].sum += tree[rs].sum;
        }
    }
    int build(R int lef, R int rig, R int d)
    {
        int mid = lef + rig >> 1; int now = mid; dim = d;
        std::nth_element(tree + lef, tree + mid, tree + 1 + rig, cmp);//O(len)科学得到中位数
        if(lef < mid) ls = build(lef, mid - 1, d ^ 1);
        if(rig > mid) rs = build(mid + 1, rig, d ^ 1);
        pushup(mid); return mid;
    }
    IN bool isok(R int now) {return a * tree[now].cood[0] + b * tree[now].cood[1] < c;}//判断当前点是否在查询范围内
    IN int judge(R int now)
    {
        int ret = 0;
        if(tree[now].lim[0][0] * a + tree[now].lim[1][0] * b < c) ++ret;//判断管辖区间的四个角
        if(tree[now].lim[0][1] * a + tree[now].lim[1][0] * b < c) ++ret;
        if(tree[now].lim[0][0] * a + tree[now].lim[1][1] * b < c) ++ret;
        if(tree[now].lim[0][1] * a + tree[now].lim[1][1] * b < c) ++ret;
        return ret;
    }
    void query(R int now)
    {
        if(isok(now)) ans += tree[now].val;
        int buf;
        if(ls)
        {
            buf = judge(ls);
            if(buf == 4) ans += tree[ls].sum;//全覆盖直接加上就好
            else if(buf) query(ls);
        }
        if(rs)
        {
            buf = judge(rs);
            if(buf == 4) ans += tree[rs].sum;
            else if(buf) query(rs);
        }
    }
    #undef ls
    #undef rs
}
int main(void)
{
    in(dot), in(q);
    for (R int i = 1; i <= dot; ++i)
    {
        in(tree[i].cood[0]), in(tree[i].cood[1]), in(tree[i].val);
        tree[i].lim[0][0] = tree[i].lim[0][1] = tree[i].cood[0];
        tree[i].lim[1][0] = tree[i].lim[1][1] = tree[i].cood[1];
        tree[i].sum = tree[i].val;
    }
    root = KDT::build(1, dot, 0);
    W (q--)
    {
        in(a), in(b), in(c); ans = 0;
        KDT::query(root);
        printf("%lld\n", ans);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值