voidinsert(int &u, int tl, int tr, int pos, int val){ // u 表示当前子树根节点, tl tr 表示当前节点区间, pos 表示插入位置, val 表示插入值 if (!u) u = ++ tTol; // 动态开点 sum[u] += val; // 处理信息 if (tl == tr) return; int mid = tl + tr >> 1; if (pos <= mid) insert(lson[u], tl, mid, pos, val); elseinsert(rson[u], mid + 1, tr, pos, val); }
注意这里的线段树根节点传入引用值.
区间修改
1 2 3 4 5 6 7 8 9
voidmodifyRange(int &u, int tl, int tr, int l, int r, int val){ if (!u) u = ++ tTol; int len = min(tr, r) - max(tl, l) + 1; // 区间重合长度 sum[u] += val * len; if (tl >= l and tr <= r) return tag[u] += val, void(); // 打入懒标记并直接返回 int mid = tl + tr >> 1; // 继续递归处理 if (l <= mid) modifyRange(lson[u], tl, mid, l, r, val); if (r > mid) modifyRange(rson[u], mid + 1, tr, l, r, val); }
单点 / 区间查询
1 2 3 4 5 6 7 8 9
LL query(int u, int tl, int tr, int l, int r){ // root, tl, tr 代表当前线段树节点信息 l, r 代表查询区间边界 if (tl >= l and tr <= r) return sum[u]; pushDown(u, tl, tr); int mid = tl + tr >> 1; LL res = 0; if (l <= mid) res += query(lson[u], tl, mid, l, r); if (r > mid) res += query(rson[u], mid + 1, tr, l, r); return res; }
int n, m, k; structEdge { int a, b; }edge[M]; structSave { int fa, fb; bool add; }st[M]; int top; vector<int> tree[N << 2]; // SegmentTree int fa[N << 1], h[N << 1];
inlineintfind(int a){ while (fa[a] ^ a) a = fa[a]; return a; }
voidmerge(int x, int y){ x = find(x), y = find(y); if (h[x] > h[y]) swap(x, y); // x to y st[++ top] = {x, y, h[x] == h[y]}; fa[x] = y; h[y] += (h[x] == h[y]); }
voidmodify(int u, int tl, int tr, int l, int r, int val){ if (tl > r or tr < l) return; if (tl >= l and tr <= r) return tree[u].push_back(val), void(); int mid = tl + tr >> 1; modify(ls, tl, mid, l, r, val); modify(rs, mid + 1 ,tr, l, r, val); }
voiddfs(int u, int tl, int tr){ bool f = true; int ptr = top; int tmpa, tmpb; for (int t : tree[u]) { tmpa = find(edge[t].a), tmpb = find(edge[t].b); if (tmpa == tmpb) { for (int i = tl; i <= tr; ++ i) puts("No"); f = false; break; } merge(edge[t].a, edge[t].b + n); merge(edge[t].a + n, edge[t].b); } if (f) { if (tl == tr) puts("Yes"); else { int mid = tl + tr >> 1; dfs(ls, tl, mid), dfs(rs, mid + 1, tr); } } for (; top > ptr; top --) { h[fa[st[top].fa]] -= st[top].add; fa[st[top].fa] = st[top].fa; } return; }
intmain(){ input(n), input(m), input(k); for (int i = 1, x, y, l, r; i <= m; ++ i) { input(x), input(y), input(l), input(r); edge[i] = {x, y}; modify(1, 1, k, l + 1, r, i); // note that max Range is k } for (int i = 1; i <= (n << 1); ++ i) fa[i] = i, h[i] = 1; // init dsu dfs(1, 1, k); return0; }
n 表示线段(一次函数)个数, 由于一条线段最多分成 $logn$ 个线段, 因此总规模为 $n \cdot logn$.
a 存储函数属性(斜率 $k$, 纵截距 $b$.
tree 存储当前线段树节点对应函数位置
1 2 3 4 5 6 7 8 9
using D = double; using PDI = pair<D, int>; constint N = 1e5 + 10; constint SX = 39989, SY = 1e9; constint T = 4e4; const D eps = 1e-9; int n, lastans, op, qxa, qya, qxb, qyb; structSegment { D k, b; }a[N]; int tree[N + (T << 1)], top;
浮点数比较函数
为防止浮点数误差, 自行写函数进行比较
1 2 3 4 5 6
intcmp(D x, D y){ // bigger return 1, smaller return -1, equal return 0 if (x - y > eps) return1; if (y - x > eps) return-1; return0; }
$pair$ 取 $max$
1 2 3 4 5
PDI pMax(PDI a, PDI b){ if (cmp(a.fi, b.fi) == -1) return b; if (cmp(a.fi, b.fi) == 1) return a; return a.se < b.se ? a : b; }
计算某一横坐标 $x$ 下的函数值
1
D calc(int i, int x){ return a[i].k * x + a[i].b; }
添加一次函数
1 2 3 4 5 6 7 8
voidadd(int xa, int ya, int xb, int yb){ if (xa == xb) a[++ top].k = 0, a[top].b = max(ya, yb); else { a[++ top].k = (D)(yb - ya) / (xb - xa), a[top].b = ya - a[top].k * xa; } return; }
更新最优斜率
1 2 3 4 5 6 7 8
voidpushDown(int rt, int tl, int tr, int u){ int &v = tree[rt], mid = tl + tr >> 1; if (cmp(calc(u, mid), calc(v, mid)) == 1) swap(u, v); int bl = cmp(calc(u, tl), calc(v, tl)); int br = cmp(calc(u, tr), calc(v, tr)); if (bl == 1or (!bl and u < v)) pushDown(lson, tl, mid, u); if (br == 1or (!br and u < v)) pushDown(rson, mid + 1, tr, u); }
将线段分裂, 挂载到树上
1 2 3 4 5 6
voidcover(int rt, int tl, int tr, int l, int r, int u){ if (l <= tl and r >= tr) returnpushDown(rt, tl, tr, u); int mid = tl + tr >> 1; if (l <= mid) cover(lson, tl, mid, l, r, u); if (r > mid) cover(rson, mid + 1, tr, l, r, u); }
查询某个横坐标 $x$ 下所有函数最大值
1 2 3 4 5 6 7
PDI query(int rt, int l, int r, int x){ if (l > x or r < x) return {0, 0}; int mid = l + r >> 1; D res = calc(tree[rt], x); if (l == r) return {res, tree[rt]}; returnpMax({res, tree[rt]}, pMax(query(lson, l, mid, x), query(rson, mid + 1, r, x))); }
voidbuild(int u, int l, int r){ tree[u] = {l, r, 0, 0}; if (l != r) { int mid = l + r >> 1; build(lson, l, mid), build(rson, mid + 1, r); } }
voidmodify(int u, int l, int r, int k){ if (l(u) > r orr(u) < l) return; if (l(u) >= l andr(u) <= r) { cnt(u) += k; PushUp(u); return; } modify(lson, l, r, k), modify(rson, l, r, k); PushUp(u); }
intmain(){ int n; cin >> n; for (int i = 0, j = 0; i < n; i ++) { double x1, x2, y1, y2; cin >> x1 >> y1 >> x2 >> y2; seg[j ++] = { x1, y1, y2, 1 }; seg[j ++] = { x2, y1, y2, -1 }; lis.push_back(y1), lis.push_back(y2); } sort(lis.begin(), lis.end()); lis.erase(unique(lis.begin(), lis.end()), lis.end()); // 离散化 sort(seg, seg + (n << 1)); build(1, 0, lis.size() - 2); // 建树 double ans = 0; for (int i = 0; i < n << 1; i ++) { if (i > 0) ans += len(1) * (seg[i].x - seg[i - 1].x); modify(1, find(seg[i].y1), find(seg[i].y2) - 1, seg[i].k); } printf("%d\n", ans); return0; }