首页 > 其他分享 >「解题报告」P8861 线段

「解题报告」P8861 线段

时间:2023-06-21 20:45:23浏览次数:68  
标签:int 线段 mid long add 解题 P8861 区间 ufs

有趣 ds 题。

首先有一个部分分 \(l_i \le 10^5 \le r_i\)。发现这相当于可以把区间分成左右两部分,那么我们可以考虑将左右分开考虑。

我们将每个区间拆开成两部分,这样插入的时候就直接插入即可,修改操作时,发现实际上就是将左端所有长度大于 \(10^5 - l\) 的区间长度改为 \(10^5 - l\),右端同理。容易发现这个是可以暴力做的,我们把长度相同的区间并查集缩起来,这样我们可以用一个堆来维护左右端,每次暴力弹出对顶元素,把它们缩起来。容易发现均摊复杂度是正确的。查询的操作实际上就是一个区间求和,所以直接拿树状数组顺便维护一下区间和即可。

那么拓展到一般情况,可以想到类似于分治的东西,即可通过上述方法覆盖到所有的区间。我们用线段树维护这个东西。

插入时,将一个区间插入到包含它且穿过中点的区间上。

修改时,考虑分几种情况:

  • 如果修改区间完全包含这个区间,那么显然不会有影响,直接返回;
  • 如果修改区间恰好穿过当前区间的中点,那么用上述的方法维护即可;
  • 如果修改区间完全包含在左区间或右区间,那么就把所有与当前修改区间相交的区间拿出来,取交之后重新插入左区间或右区间。发现,每一个区间最多会被这样插入 \(O(\log n)\) 次,所以这里均摊复杂度也是对的。

总复杂度 \(O(n \log^2 n)\)。

具体实现上,我们需要记录左端的每个点与右端对应的点,删除时可以给两个点打一个删除标记;为了遍历一个并查集中的所有数,我们同时需要维护一个链表,链表容易遍历与合并。

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 200005, MAXM = MAXN * 120;
int q, type;
long long lstans;
struct UnionFindSet {
    int fa[MAXM], siz[MAXM], tot;
    int cnt[MAXM], id[MAXM], pt[MAXM];
    int ed[MAXM], nxt[MAXM];
    int pos[MAXM];
    int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }
    int newNode(int k, int p) {
        int u = ++tot;
        fa[u] = ed[u] = u, siz[u] = cnt[u] = 1, pt[k] = u, id[u] = k, pos[u] = p;
        return u;
    }
    void delNode(int u) {
        if (id[u]) id[u] = 0, cnt[find(u)]--;
    }
    int merge(int x, int y) {
        if (!x || !y) return x + y;
        if (siz[x] > siz[y]) swap(x, y);
        fa[x] = y, siz[y] += siz[x], cnt[y] += cnt[x];
        nxt[ed[y]] = x, ed[y] = ed[x];
        return y;
    }
} ufs;
struct BinaryIndexTree {
    long long a[MAXN], b[MAXN];
#define lowbit(x) (x & (-x))
    void add(long long *a, int d, long long v) {
        while (d < MAXN) {
            a[d] += v;
            d += lowbit(d);
        }
    }
    long long query(long long *a, int d) {
        long long ret = 0;
        while (d) {
            ret += a[d];
            d -= lowbit(d);
        }
        return ret;
    }
    void add(int d, long long v) {
        add(a, d, v), add(b, d, d * v);
    }
    void add(int a, int b, long long v) {
        add(a, v), add(b, -v);
    }
    long long query(int l, int r) {
        return (r * query(a, r) - query(b, r)) - (l * query(a, l) - query(b, l));
    }
} bit;
struct SegmentTree {
    struct Node {
        priority_queue<pair<int, int>, vector<pair<int, int>>, greater<pair<int, int>>> l;
        priority_queue<pair<int, int>> r;
    } t[MAXN << 3];
#define lc (i << 1)
#define rc (i << 1 | 1)
    void addEdge(int a, int b, int k, int i = 1, int l = 1, int r = 200000) {
        if (l == r) return;
        int mid = (l + r) >> 1;
        if (b < mid) return addEdge(a, b, k, lc, l, mid);
        if (a > mid) return addEdge(a, b, k, rc, mid, r);
        t[i].l.push({ a, ufs.newNode(k << 1, a) });
        t[i].r.push({ b, ufs.newNode(k << 1 | 1, b) });
        bit.add(a, b, 1);
    }
    void update(int a, int b, int i = 1, int l = 1, int r = 200000) {
        if (r - l == 1 || a <= l && r <= b) return;
        int mid = (l + r) >> 1;
        if (b < mid) {
            while (t[i].l.size()) {
                auto p = t[i].l.top();
                if (p.first > b) break;
                t[i].l.pop();
                int x = p.second;
                do {
                    if (ufs.id[x]) {
                        int y = ufs.id[x];
                        ufs.delNode(x), ufs.delNode(ufs.pt[y ^ 1]);
                        bit.add(ufs.pos[ufs.find(x)], ufs.pos[ufs.find(ufs.pt[y ^ 1])], -1);
                        addEdge(max(a, p.first), b, y / 2, lc, l, mid);
                    }
                } while (x = ufs.nxt[x]);
            }
            update(a, b, lc, l, mid);
        } else if (a > mid) {
            while (t[i].r.size()) {
                auto p = t[i].r.top();
                if (p.first < a) break;
                t[i].r.pop();
                int x = p.second;
                do {
                    if (ufs.id[x]) {
                        int y = ufs.id[x];
                        ufs.delNode(x), ufs.delNode(ufs.pt[y ^ 1]);
                        bit.add(ufs.pos[ufs.find(ufs.pt[y ^ 1])], ufs.pos[ufs.find(x)], -1);
                        addEdge(a, min(p.first, b), y / 2, rc, mid, r);
                    }
                } while (x = ufs.nxt[x]);
            }
            update(a, b, rc, mid, r);
        } else {
            {
                int x = 0;
                while (t[i].l.size()) {
                    auto p = t[i].l.top();
                    if (p.first > a) break;
                    bit.add(p.first, a, -ufs.cnt[p.second]);
                    x = ufs.merge(x, p.second);
                    t[i].l.pop();
                }
                if (x) {
                    t[i].l.push({ a, x });
                    ufs.pos[x] = a;
                }
            }
            {
                int x = 0;
                while (t[i].r.size()) {
                    auto p = t[i].r.top();
                    if (p.first < b) break;
                    bit.add(b, p.first, -ufs.cnt[p.second]);
                    x = ufs.merge(x, p.second);
                    t[i].r.pop();
                }
                if (x) {
                    t[i].r.push({ b, x });
                    ufs.pos[x] = b;
                }
            }
            update(a, b, lc, l, mid), update(a, b, rc, mid, r);
        }
    }
} st;
int main() {
    scanf("%d%d", &q, &type);
    int ecnt = 0;
    while (q--) {
        int op, l, r; scanf("%d%d%d", &op, &l, &r);
        l = (l + lstans * type) % 200001, r = (r + lstans * type) % 200001;
        if (op == 1) st.addEdge(l, r, ++ecnt);
        if (op == 2) st.update(l, r);
        if (op == 3) printf("%lld\n", lstans = bit.query(l, r));
    }
    return 0;
}

标签:int,线段,mid,long,add,解题,P8861,区间,ufs
From: https://www.cnblogs.com/apjifengc/p/17497128.html

相关文章

  • 计算几何之两条线段的交点
    1.概述可以通过线段的跨立实验[1]判断两条线段是否相交,但是想要进一步求它们的交点还是比较麻烦。[2]给出的方法更加简单,其原理来自求三维空间两条线段的交点[3]。为了更好的理解,本文将详细介绍二维空间两条线段的交点求解过程。2.两条线段交点求解过程给定两条线段\(\overl......
  • 浅谈线段树
    线段树引入线段树是较为常用的数据结构,一般用于维护区间信息。线段树可以在\(O(\logn)\)的时间复杂度内实现单点修改,区间修改,区间查询等操作。一般的在区间上进行操作的题目都可以考虑线段树。普通线段树基本思想线段树,顾名思义,就是由线段组成的树。我们结合图来理解一......
  • CF542C 解题分析
    1题目大意1.1题目翻译:给定一个值域为\([1,n]\)的函数\(f(x)\),让你求出最小的\(k\),其中\(k\)满足\(f^{(2k)}(x)=f^{(k)}(x)\)。其实我觉得这题你谷翻译十分到位,建议没读懂题的还是去看你谷翻译罢。1.2数据范围:对于\(100\%\)的数据:\(1\leqn\leq200\)1.3......
  • 使用MaskableGraphic画线段-生成Mesh方式
    usingSystem.Collections;usingSystem.Collections.Generic;usingUnityEngine;usingUnityEngine.UI;usingUnityEngine.EventSystems;publicclassUGUIPoplateMesh:MaskableGraphic,IPointerEnterHandler,IPointerExitHandler{protectedoverridevoidO......
  • [NOIP2020] 移球游戏 解题报告
    题目描述给定\(n+1\)个栈,栈的高度限制为\(m\)。初始时前\(n\)个上每个有\(m\)个球,最后一个为空。球分为\(n\)种颜色,每种恰好\(m\)个。一次操作可以把一个栈顶的元素弹出放到一个另一个栈顶,但是不可以使栈溢出或下溢。现要把同种颜色的球移动到同一个栈上,你需要构造一......
  • [网络安全] DVWA之 Command Injection 攻击姿势及解题详析合集
    CommandInjection命令注入(CommandInjection)是一种安全漏洞,发生在应用程序使用用户提供的输入作为系统命令的一部分而未经充分验证和过滤的情况下。当应用程序在构造系统命令时,如果没有对用户输入进行适当的验证和过滤,攻击者可以通过在用户输入中插入恶意命令来执行任意系统命......
  • CF521E Cycling City 解题报告
    题面一道难得恰到好处的构造题。分析因为要构造三条从\(s\)到\(t\)的路径,且三条路径中任意两条路径经过的点集的交集等于\(\{s,t\}\)。我们知道当两条路径经过的点集的交集等于\(\{s,t\}\)时,这两条路径将会构成一个环。因此题意转化为要求我们找到两个经过的边集有重合......
  • 凌乱的yyy / 线段覆盖
    题目背景快noip了,yyy很紧张!题目描述现在各大oj上有\(n\)个比赛,每个比赛的开始、结束的时间点是知道的。yyy认为,参加越多的比赛,noip就能考的越好(假的)。所以,他想知道他最多能参加几个比赛。由于yyy是蒟蒻,如果要参加一个比赛必须善始善终,而且不能同时参加\(2\)个......
  • [网络安全] DVWA之CSRF攻击姿势及解题详析合集
    CSRFCSRF(Cross-SiteRequestForgery,跨站请求伪造)是一种常见的Web应用程序安全漏洞,它利用了用户在已认证的网站中的身份,通过欺骗用户发起非预期的请求。攻击者会构造一个恶意网页,使用户在浏览器中访问该网页时,自动向目标网站发送了未经用户授权的请求。CSRF攻击的原理是利用了W......
  • Luogu3792 由乃与大母神原型和偶像崇拜 - 线段树 - set -
    题目链接:https://www.luogu.com.cn/problem/P3792题解:一点小小的空间震撼(ML:125MB)首先询问可以转化成:区间\([l,r]\)的\(max-min+1=r-l+1\)并且区间内没有重复元素。可以考虑对每个点\(i\)记一个前驱结点\(pr_i\),表示\(a_{pr_i}=a_i\),且\(pr_i\)是\(i\)前面离\(i\)......