洛谷传送门
BZOJ传送门
题目描述
巧克力王国里的巧克力都是由牛奶和可可做成的。但是并不是每一块巧克力都受王国人民的欢迎,因为大家都不喜欢过于甜的巧克力。
对于每一块巧克力,我们设 x x 和 为其牛奶和可可的含量。由于每个人对于甜的程度都有自己的评判标准,所以每个人都有两个参数 a a 和 ,分别为他自己为牛奶和可可定义的权重, 因此牛奶和可可含量分别为 x x 和 的巧克力对于他的甜味程度即为 ax+by a x + b y 。而每个人又有一个甜味限度 c c ,所有甜味程度大于等于 的巧克力他都无法接受。每块巧克力都有一个美味值 h h 。
现在我们想知道对于每个人,他所能接受的巧克力的美味值之和为多少。
输入输出格式
输入格式:
第一行两个正整数 和
m
m
,分别表示巧克力个数和询问个数。
接下来 行,每行三个整数
x , y , h
x
,
y
,
h
,含义如题目所示。
再接下来
m
m
行,每行三个整数 ,含义如题目所示。
输出格式:
输出 m m 行,其中第 行表示第 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%的数据, 。
解题分析
KD−Tree 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);
}
}