Bzoj 3165. [Heoi2013]Segment 李超线段树(模板题)

1 篇文章 0 订阅

题目大意:

要求在平面直角坐标系下维护两个操作:
1.在平面上加入一条线段。记第i条被插入的线段的标号为i。
2.给定一个数k,询问与直线 x = k相交的线段中,交点最靠上的线段的编号

AC代码:

#include <iostream>
#include <cstring>
#define mod1 39989
#define mod2 1000000000
#define endl '\n'
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define ls(rt) rt << 1
#define rs(rt) rt << 1 | 1
using namespace std;
typedef pair <double, int> pdi;
const int maxn = 2e5 + 10;
int cnt = 0;
struct L {
    double k, b;
} line[maxn];
struct segtree {
    int id, l, r;
} sgt[maxn << 1];
int sgt_cnt = 0;
double cal(int id, int x) {return line[id].k * x + line[id].b;}
void add(int x1, int y1, int x2, int y2) {
    if (x1 == x2) line[++cnt].k = 0, line[cnt].b = max(y1, y2);
    else line[++cnt].k = 1.0 * (y1 - y2) / (x1 - x2), line[cnt].b = y1 - line[cnt].k * x1;
}
void update(int lc, int rc, int &rt, int l, int r, int u) {
    if (!rt) rt = ++sgt_cnt;
    int mi = (lc + rc) >> 1;
    if (r < lc || l > rc) return;
    /*if (lc == rc) {
        sgt[rt] = u;
        return;
    }*/ //这里应该要判断是否在范围内先
    int v = sgt[rt].id;
    double resu = cal(u, mi), resv = cal(sgt[rt].id, mi);
    if (l <= lc && rc <= r) {
        if (lc == rc) {
            if (resu > resv) sgt[rt].id = u;
            return;
        }
        if (line[u].k > line[v].k) {
            if (resu > resv) {
                sgt[rt].id = u;
                //update(lc, mi, ls(rt), l, r, u);
                update(lc, mi, sgt[rt].l, l, r, v);
                //运用到可持久化标记的思想,所以u的标记并没有下传,只要询问时统计一遍路径上的点即可
                //或者可以这样想,如果在[lc,mi]区间v比u更优,但是没有拿v来更新,所以就统计不到v,此时就要传v下去更新
            }
            else 
                update(mi + 1, rc, sgt[rt].r, l, r, u);
        }
        else if (line[u].k < line[v].k) {
            if (resu > resv) {
                sgt[rt].id = u;
                //update(mi + 1, rc, rs(rt), l, r, u);
                update(mi + 1, rc, sgt[rt].r, l , r, v);
            }
            else 
                update(lc, mi, sgt[rt].l, l, r, u);
        }
        else {
            if (line[u].b > line[v].b)
                sgt[rt].id = u; 
        }
        return;
    }
    update(lc, mi, sgt[rt].l, l, r, u);
    update(mi + 1, rc, sgt[rt].r, l, r, u);
}
pdi pmax(pdi A, pdi B) {
    if (A.first > B.first) return A;
    else if (A.first < B.first) return B;
    else if (A.second < B.second) return A;
    else return B;
}
pdi query(int l, int r, int rt, int k) {
    if (r < k || k < l) return {0, 0};
    int mi = (l + r) >> 1;
    double res = cal(sgt[rt].id, k);
    if (l == r) return {res, sgt[rt].id};
    return pmax({res, sgt[rt].id}, pmax(query(l, mi, sgt[rt].l, k), query(mi + 1, r, sgt[rt].r, k)));
}
int n, op, k, x1, x2, y1, y2, x, lastans = 0, rt = 0;
int main() {
    IOS;
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> op;
        if (!op) {
            cin >> k;
            x = (k + lastans - 1) % mod1 + 1;
            cout << (lastans = query(1, mod1, 1, x).second) << endl;
        }
        else {
            cin >> x1 >> y1 >> x2 >> y2;
            x1 = (x1 + lastans - 1) % mod1 + 1,
            y1 = (y1 + lastans - 1) % mod2 + 1,
            x2 = (x2 + lastans - 1) % mod1 + 1, 
            y2 = (y2 + lastans - 1) % mod2 + 1;
            if (x1 > x2) swap(x1, x2), swap(y1, y2);
            add(x1, y1, x2, y2);
            update(1, mod1, rt, x1, x2, cnt);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值