社区讨论
82pts 求卡常
P3122 [USACO15FEB] Fencing the Herd G参与者 2已保存回复 6
讨论操作
快速查看讨论及其快照的属性,并进行相关操作。
- 当前回复
- 6 条
- 当前快照
- 1 份
- 快照标识符
- @mhjug80l
- 此快照首次捕获于
- 2025/11/04 08:41 4 个月前
- 此快照最后确认于
- 2025/11/04 08:41 4 个月前
[最新的一个评测记录]。(https://www.luogu.com.cn/record/213387761)
目前 82pts T 3 个点。
思路是直接用非旋 Treap 动态维护上下凸包。
由于为了卡常写了一堆乱七八糟的代码,我会加注释的。
CPP#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int inf = 0x3f3f3f3f;
#define max(x, y) ((x) > (y) ? (x) : (y))
#define min(x, y) ((x) < (y) ? (x) : (y))
struct IO {
#define MAXSIZE (1 << 20)
#define isdigit(x) (x >= '0' && x <= '9')
char buf[MAXSIZE], *p1, *p2;
char pbuf[MAXSIZE], *pp;
#if DEBUG
#else
IO() : p1(buf), p2(buf), pp(pbuf) {}
~IO() { fwrite(pbuf, 1, pp - pbuf, stdout); }
#endif
char gc() {
#if DEBUG // 调试,可显示字符
return getchar();
#endif
if (p1 == p2) p2 = (p1 = buf) + fread(buf, 1, MAXSIZE, stdin);
return p1 == p2 ? ' ' : *p1++;
}
bool blank(char ch) {
return ch == ' ' || ch == '\n' || ch == '\r' || ch == '\t';
}
template <class T>
T read(T &x) {
double tmp = 1;
bool sign = 0;
x = 0;
char ch = gc();
for (; !isdigit(ch); ch = gc())
if (ch == '-') sign = 1;
for (; isdigit(ch); ch = gc()) x = x * 10 + (ch - '0');
if (ch == '.')
for (ch = gc(); isdigit(ch); ch = gc())
tmp /= 10.0, x += tmp * (ch - '0');
if (sign) x = -x;
return x;
}
void read(char *s) {
char ch = gc();
for (; blank(ch); ch = gc());
for (; !blank(ch); ch = gc()) *s++ = ch;
*s = 0;
}
void read(char &c) { for (c = gc(); blank(c); c = gc()); }
void push(const char &c) {
#if DEBUG // 调试,可显示字符
putchar(c);
#else
if (pp - pbuf == MAXSIZE) fwrite(pbuf, 1, MAXSIZE, stdout), pp = pbuf;
*pp++ = c;
#endif
}
template <class T>
void write(T x) {
if (x < 0) x = -x, push('-'); // 负数输出
static T sta[35];
T top = 0;
do {
sta[top++] = x % 10, x /= 10;
} while (x);
while (top) push(sta[--top] + '0');
}
template <class T>
void write(T x, char lastChar) {
write(x), push(lastChar);
}
} io;
int n, m, rt[2], idx[2], maxx = -2147483648, minx = 2147483647;
ll c;
struct node {
int ls, rs, sz, x, y, pr;
}tr[2][400001];//平衡树(凸包),按照 x 排序 tr[0]是上凸包 tr[1]是下凸包
inline void split(int op, int x, int k, int &lrt, int &rrt) {
if (! x) return lrt = rrt = 0, void();
if (tr[op][x].x <= k) lrt = x, split(op, tr[op][x].rs, k, tr[op][x].rs, rrt);
else rrt = x, split(op, tr[op][x].ls, k, lrt, tr[op][x].ls);
tr[op][x].sz = tr[op][tr[op][x].ls].sz + tr[op][tr[op][x].rs].sz + 1;
}
inline ll merge(int op, int lrt, int rrt) {
if ((! lrt) || (! rrt)) return lrt | rrt;
if (tr[op][lrt].pr > tr[op][rrt].pr) return tr[op][lrt].rs = merge(op, tr[op][lrt].rs, rrt), tr[op][lrt].sz = tr[op][tr[op][lrt].ls].sz + tr[op][tr[op][lrt].rs].sz + 1, lrt;
return tr[op][rrt].ls = merge(op, lrt, tr[op][rrt].ls), tr[op][rrt].sz = tr[op][tr[op][rrt].ls].sz + tr[op][tr[op][rrt].rs].sz + 1, rrt;
}
inline ll rkgetx(int op, int x, int rk) {//给定排名 rk 获得 x 第 rk 小的点
if (rk == tr[op][tr[op][x].ls].sz + 1) return x;
if (rk <= tr[op][tr[op][x].ls].sz) return rkgetx(op, tr[op][x].ls, rk);
return rkgetx(op, tr[op][x].rs, rk - tr[op][tr[op][x].ls].sz - 1);
}
inline void insert_tb(ll op, ll x, ll y) {//将点插入凸包
int lrt, rrt, trt;
split(op, rt[op], x - 1, lrt, rrt);
split(op, rrt, x, trt, rrt);
if (trt && ((tr[op][trt].y > y && !op) || (tr[op][trt].y < y && op))) {//判断有无与自己 x 相同的点,留下更高/更低的点
merge(op, lrt, merge(op, trt, rrt));
return ;
}
tr[op][++ idx[op]] = {0, 0, 1, x, y, rand() | (rand() << 16)};
ll sx, sy;
if (tr[op][lrt].sz && tr[op][rrt].sz) {
int ls = rkgetx(op, lrt, tr[op][lrt].sz), rs = rkgetx(op, rrt, 1);
sx = 1ll * (tr[op][idx[op]].x - tr[op][ls].x) * (tr[op][rs].y - tr[op][idx[op]].y), sy = 1ll * (tr[op][idx[op]].y - tr[op][ls].y) * (tr[op][rs].x - tr[op][idx[op]].x);
if (ls && rs && ((sy <= sx && !op) || (op && sy >= sx))) {
idx[op] --, merge(op, lrt, rrt);
return ;
}
}
int l = 1, r = tr[op][lrt].sz - 1, ans = r + 1, mid, tm, tm1;// 二分这个点左边有哪些点会被删掉
while (l <= r) {
mid = l + r >> 1, tm = rkgetx(op, lrt, mid), tm1 = rkgetx(op, lrt, mid + 1);
sx = 1ll * (tr[op][tm1].x - tr[op][tm].x) * (tr[op][idx[op]].y - tr[op][tm1].y), sy = 1ll * (tr[op][tm1].y - tr[op][tm].y) * (tr[op][idx[op]].x - tr[op][tm1].x);
//斜率我怕精度炸同时除法时间长就移项换成乘法了。a / b < c / d -> a * d < c * b (b,d>0)
if (op ^ (sy <= sx)) ans = mid, r = mid - 1;
else l = mid + 1;
}
if (ans < tr[op][lrt].sz) trt = rkgetx(op, lrt, ans), split(op, lrt, tr[op][trt].x, lrt, trt);
l = 2, r = tr[op][rrt].sz, ans = 1;// 二分这个点右边有哪些点会被删掉
while (l <= r) {
mid = l + r >> 1, tm = rkgetx(op, rrt, mid), tm1 = rkgetx(op, rrt, mid - 1);
sx = 1ll * (tr[op][tm].x - tr[op][tm1].x) * (tr[op][tm1].y - tr[op][idx[op]].y), sy = 1ll * (tr[op][tm].y - tr[op][tm1].y) * (tr[op][tm1].x - tr[op][idx[op]].x);
if (op ^ (sx <= sy)) ans = mid, l = mid + 1;
else r = mid - 1;
}
if (ans > 1) trt = rkgetx(op, rrt, ans), split(op, rrt, tr[op][trt].x - 1, trt, rrt);
//删掉之后加点
rt[op] = merge(op, lrt, merge(op, idx[op], rrt));
}
inline ll fd(int op, int a, int b) {//找到最大/最小的ax+by。
int l = 1, r = tr[op][rt[op]].sz - 1, ans = r + 1, mid, tm, tm1;
ll sx, sy;
//找到斜率第一个大于/小于-a/b的点
while (l <= r) {
mid = l + r >> 1, tm = rkgetx(op, rt[op], mid), tm1 = rkgetx(op, rt[op], mid + 1);
sx = -1ll * a * (tr[op][tm1].x - tr[op][tm].x), sy = 1ll * b * (tr[op][tm1].y - tr[op][tm].y);
//同样移项过了。处理过了保证 b > 0。
if (op ^ (sy < sx)) ans = mid, r = mid - 1;
else l = mid + 1;
}
ans = rkgetx(op, rt[op], ans);
return 1ll * tr[op][ans].x * a + 1ll * tr[op][ans].y * b;
}
signed main() {
// freopen ("P3122_10.in", "r", stdin);
// freopen ("sb.out", "w", stdout);
// system ("fc sb.out P3122_10.out");
srand(114514);
n = io.read(n), m = io.read(m);
int x, y;
while(n -- ) {
x = io.read(x), y = io.read(y);
insert_tb(0, x, y), insert_tb(1, x, y);//插入凸包
maxx = max(maxx, x), minx = min(minx, x);//当 b = 0 时方便处理
}
int op, a, b;
while (m -- ) {
op = io.read(op), a = io.read(a), b = io.read(b);
if (op & 1) {
insert_tb(0, a, b), insert_tb(1, a, b);//插入凸包
maxx = max(maxx, a), minx = min(minx, a);//当 b = 0 时方便处理
}
else {
c = io.read(c);
if (!b) {
if (a < 0) a = -a, c = -c;//移项方便
if (1ll * maxx * a < c || 1ll * minx * a > c) putchar('Y'), putchar('E'), putchar('S'), putchar('\n');
else putchar('N'), putchar('O'), putchar('\n');
}
else {
if (b < 0) a = -a, b = -b, c = -c;//移项方便
if (fd(0, a, b) < c || fd(1, a, b) > c) putchar('Y'), putchar('E'), putchar('S'), putchar('\n');
else putchar('N'), putchar('O'), putchar('\n');
}
}
}
}
回复
共 6 条回复,欢迎继续交流。
正在加载回复...