详细介绍:线段树刷题记录

一篇讲解很好的线段树博客:数据结构--线段树篇_数据结构线段树-CSDN博客

一、区间查询 无修改:

(一)最值问题:

1.P1816 忠诚 - 洛谷
思路:

        模板。

注意:

        无。

代码:
#include  #define ioscc ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)#define endl '\n'#define me(a, x) memset(a, x, sizeof a)#define all(a) a.begin(), a.end()#define sz(a) ((int)(a).size())#define pb(a) push_back(a)using namespace std; typedef unsigned long long ull;typedef long long ll;typedef pair pii;typedef vector> vvi;typedef vector vi;typedef vector vb; const int dx[4] = {-1, 0, 1, 0};const int dy[4] = {0, 1, 0, -1};const int MAX = (1ll ostream &operator &a) noexcept{    for (int i = 0; i istream &operator>>(istream &in, vector &a) noexcept{    for (int i = 0; i > a[i];    return in;} /* ----------------- 有乘就强转,前缀和开ll ----------------- */ int v[N];struct Node{    int l, r;    int minn;} tr[N * 4]; void pushup(int u){    tr[u].minn = min(tr[u > 1;    build(u = l && tr[u].r > 1;    int minn = MAX;    if (l  mid)        minn = min(minn, query(u > n >> m;    for (int i = 1; i > v[i];     build(1, 1, n);     while (m--)    {        int l, r;        cin >> l >> r;        cout << query(1, l, r) << ' ';    }    cout << endl;} int main(){    ioscc;     solve();     return 0;}
2.P1886 滑动窗口 /【模板】单调队列 - 洛谷
思路:

        模板。

注意:

        无。

代码:
#include  #define ioscc ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)#define endl '\n'#define me(a, x) memset(a, x, sizeof a)#define all(a) a.begin(), a.end()#define sz(a) ((int)(a).size())#define pb(a) push_back(a)using namespace std; typedef unsigned long long ull;typedef long long ll;typedef pair pii;typedef vector> vvi;typedef vector vi;typedef vector vb; const int dx[4] = {-1, 0, 1, 0};const int dy[4] = {0, 1, 0, -1};const int MAX = (1ll ostream &operator &a) noexcept{    for (int i = 0; i istream &operator>>(istream &in, vector &a) noexcept{    for (int i = 0; i > a[i];    return in;} /* ----------------- 有乘就强转,前缀和开ll ----------------- */ int v[N];struct Node{    int l, r;    int minn, maxx;} tr[N * 4]; void pushup(int u){    tr[u].minn = min(tr[u > 1;    build(u = l && tr[u].r > 1;    int minn = MAX;    if (l  mid)        minn = min(minn, queryMin(u = l && tr[u].r > 1;    int maxx = MIN;    if (l  mid)        maxx = max(maxx, queryMax(u > n >> k;     for (int i = 1; i > v[i];     build(1, 1, n);     for (int i = 1; i <= n - k + 1; ++i)        cout << queryMin(1, i, i + k - 1) << ' ';    cout << endl;    for (int i = 1; i <= n - k + 1; ++i)        cout << queryMax(1, i, i + k - 1) << ' ';    cout << endl;} int main(){    ioscc;     solve();     return 0;}

(二)区间gcd问题:

1.P1890 gcd区间 - 洛谷
思路:

        模板。

注意:

        无。

代码:
#include  #define ioscc ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)#define endl '\n'#define me(a, x) memset(a, x, sizeof a)#define all(a) a.begin(), a.end()#define sz(a) ((int)(a).size())#define pb(a) push_back(a)using namespace std; typedef unsigned long long ull;typedef long long ll;typedef pair pii;typedef vector> vvi;typedef vector vi;typedef vector vb; const int dx[4] = {-1, 0, 1, 0};const int dy[4] = {0, 1, 0, -1};const int MAX = (1ll ostream &operator &a) noexcept{    for (int i = 0; i istream &operator>>(istream &in, vector &a) noexcept{    for (int i = 0; i > a[i];    return in;} /* ----------------- 有乘就强转,前缀和开ll ----------------- */ int gcd(int a, int b){    return b ? gcd(b, a % b) : a;} int v[N];struct Node{    int l, r;    int gcdd;} tr[N > 1;    build(u = l && tr[u].r > 1;    int gcdd = 0;    if (l  mid)        gcdd = gcd(gcdd, query(u > n >> m;     for (int i = 1; i > v[i];     build(1, 1, n);     while (m--)    {        int l, r;        cin >> l >> r;         cout << query(1, l, r) << endl;    }} int main(){    ioscc;     solve();     return 0;}

二、区间查询 单点修改:

(一)区间和问题:

1.P2068 统计和 - 洛谷
思路:

        模板。

注意:

        无。

代码:
#include  #define ioscc ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)#define endl '\n'#define me(a, x) memset(a, x, sizeof a)#define all(a) a.begin(), a.end()#define sz(a) ((int)(a).size())#define pb(a) push_back(a)using namespace std; typedef unsigned long long ull;typedef long long ll;typedef pair pii;typedef vector> vvi;typedef vector vi;typedef vector vb; const int dx[4] = {-1, 0, 1, 0};const int dy[4] = {0, 1, 0, -1};const int MAX = (1ll ostream &operator &a) noexcept{    for (int i = 0; i istream &operator>>(istream &in, vector &a) noexcept{    for (int i = 0; i > a[i];    return in;} /* ----------------- 有乘就强转,前缀和开ll ----------------- */ struct Node{    int l, r;    ll sum;} tr[N * 4]; void pushup(int u){    tr[u].sum = tr[u > 1;    build(u = l && tr[u].r > 1;    ll sum = 0;    if (l  mid)        sum += query(u > 1;        if (x > n >> m;     build(1, 1, n);    while (m--)    {        char op;        int a, b;        cin >> op >> a >> b;        if (op == 'x')            update(1, a, b);        else            cout << query(1, a, b) << endl;    }} int main(){    ioscc;     solve();     return 0;}
2.P2184 贪婪大陆 - 洛谷
思路:

        区间修改时使用一种类差分的思想,每次埋地雷的时候只在区间左右端点累加一次值,这样就将问题装换为了单点修改;查询时我们再使用前缀和思想统计区间 [l, r] 区间内的地雷数。具体实现就是使用线段树维护两个sum,既区间左端点的地雷和区间右端点的地雷;在查询区间 [l ,r] 时,就可以用 [1, r] 的起点数减去 [1, l] 的终点数。

注意:

        无。

代码:
#include  #define ioscc ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)#define endl '\n'#define me(a, x) memset(a, x, sizeof a)#define all(a) a.begin(), a.end()#define sz(a) ((int)(a).size())#define pb(a) push_back(a)#define ls u  pii;typedef vector> vvi;typedef vector vi;typedef vector vb; const int dx[4] = {-1, 0, 1, 0};const int dy[4] = {0, 1, 0, -1};const int MAX = (1ll ostream &operator &a) noexcept{    for (int i = 0; i istream &operator>>(istream &in, vector &a) noexcept{    for (int i = 0; i > a[i];    return in;} /* ----------------- 有乘就强转,前缀和开ll ----------------- */ struct Node{    int l, r;    int sum[2];} tr[N * 4]; void pushup(int u, int k){    tr[u].sum[k] = tr[u > 1;    build(u = l && tr[u].r > 1;    ll sum = 0;    if (l  mid)        sum += query(u > 1;    if (x > n >> m;     build(1, 1, n);     while (m--)    {        int op, l, r;        cin >> op >> l >> r;         if (op == 1)            update(1, l, 0), update(1, r, 1);        else            cout << query(1, 1, r, 0) - query(1, 1, l - 1, 1) << endl;    }} int main(){    ioscc;     solve();     return 0;}

(二)最值问题

1.P1198 [JSOI2008] 最大数 - 洛谷
思路:

        模板。

注意:

        虽然线段树初始为空的,也要初始化 m 个位置为后续做准备。

代码:
#include  #define ioscc ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)#define endl '\n'#define me(a, x) memset(a, x, sizeof a)#define all(a) a.begin(), a.end()#define sz(a) ((int)(a).size())#define pb(a) push_back(a)using namespace std; typedef unsigned long long ull;typedef long long ll;typedef pair pii;typedef vector> vvi;typedef vector vi;typedef vector vb; const int dx[4] = {-1, 0, 1, 0};const int dy[4] = {0, 1, 0, -1};const int MAX = (1ll ostream &operator &a) noexcept{    for (int i = 0; i istream &operator>>(istream &in, vector &a) noexcept{    for (int i = 0; i > a[i];    return in;} /* ----------------- 有乘就强转,前缀和开ll ----------------- */ struct Node{    int l, r;    ll maxx;} tr[N > 1;    build(u = l && tr[u].r > 1;    ll maxx = MIN;    if (l  mid)        maxx = max(maxx, query(u > 1;    if (x > m >> mod;     build(1, 1, m);     int last = 0;    while (m--)    {        char op;        int x;        cin >> op >> x;        if (op == 'Q')        {            last = query(1, n - x + 1, n);            cout << last << endl;        }        else        {            ++n;            int ans = ((ll)x + last) % mod;            update(1, n, ans);        }    }} int main(){    ioscc;     solve();     return 0;}

三、区间查询 区间修改:

(一)区间和问题:

1.P2357 守墓人 - 洛谷
思路:

        模板。

注意:

        开ll。

代码:
#include  #define ioscc ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)#define endl '\n'#define me(a, x) memset(a, x, sizeof a)#define all(a) a.begin(), a.end()#define sz(a) ((int)(a).size())#define pb(a) push_back(a)using namespace std; typedef unsigned long long ull;typedef long long ll;typedef pair pii;typedef vector> vvi;typedef vector vi;typedef vector vb; const int dx[4] = {-1, 0, 1, 0};const int dy[4] = {0, 1, 0, -1};const int MAX = (1ll ostream &operator &a) noexcept{    for (int i = 0; i istream &operator>>(istream &in, vector &a) noexcept{    for (int i = 0; i > a[i];    return in;} /* ----------------- 有乘就强转,前缀和开ll ----------------- */ ll v[N];struct Node{    int l, r;    ll sum;    ll add;} tr[N * 4]; void pushup(int u){    tr[u].sum = tr[u > 1;    build(u = l && tr[u].r > 1;    ll sum = 0;    if (l  mid)        sum += query(u = l && tr[u].r > 1;    if (l  mid)        update(u > n >> m;     for (int i = 1; i > v[i];     build(1, 1, n);    while (m--)    {        int op;        int l, r, v;        cin >> op;        if (op == 1)        {            cin >> l >> r >> v;            update(1, l, r, v);        }        else if (op == 2)        {            cin >> v;            update(1, 1, 1, v);        }        else if (op == 3)        {            cin >> v;            update(1, 1, 1, -v);        }        else if (op == 4)        {            cin >> l >> r;            cout << query(1, l, r) << endl;        }        else            cout << query(1, 1, 1) << endl;    }} int main(){    ioscc;     solve();     return 0;}

(二)区间最值+区间和问题:

1.P3130 [USACO15DEC] Counting Haybale P - 洛谷
思路:

        线段树维护区间、最小值、区间和、懒标记。

注意:

        更新懒标记时也需将节点的最小值加上懒标记的值。

代码:
#include  #define ioscc ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)#define endl '\n'#define me(a, x) memset(a, x, sizeof a)#define all(a) a.begin(), a.end()#define sz(a) ((int)(a).size())#define pb(a) push_back(a)using namespace std; typedef unsigned long long ull;typedef long long ll;typedef pair pii;typedef vector> vvi;typedef vector vi;typedef vector vb; const int dx[4] = {-1, 0, 1, 0};const int dy[4] = {0, 1, 0, -1};const int MAX = (1ll ostream &operator &a) noexcept{    for (int i = 0; i istream &operator>>(istream &in, vector &a) noexcept{    for (int i = 0; i > a[i];    return in;} /* ----------------- 有乘就强转,前缀和开ll ----------------- */ ull v[N];struct Node{    int l, r;    ull minn;    ull sum;    ull add;} tr[N * 4]; void pushup(int u){    tr[u].minn = min(tr[u > 1;    build(u = l && tr[u].r > 1;    ull sum = 0;    if (l  mid)        sum += querySum(u = l && tr[u].r > 1;    ull minn = MAX;    if (l  mid)        minn = min(minn, queryMin(u = l && tr[u].r > 1;    if (l  mid)        update(u > n >> m;     for (int i = 1; i > v[i];     build(1, 1, n);     while (m--)    {        char op;        int a, b, c;        cin >> op;        if (op == 'M')        {            cin >> a >> b;            cout > a >> b >> c;            update(1, a, b, c);        }        else        {            cin >> a >> b;            cout << querySum(1, a, b) << endl;        }    }} int main(){    ioscc;     solve();     return 0;}

(三)区间和+区间乘问题:

1.P3373 【模板】线段树 2 - 洛谷
思路:

        维护乘和加两个懒标记,由于乘法优先级高于加法,所以当前节点的值为 sum * mul + add,

当父节点下传懒标记时,设 m,a 为父节点下传的乘法与加法懒标记,所以当前节点值为 (sum *

mul + add) * m + a,可得 sum * mul * m + add * m + a ,所以mul和sum的更新值为 mul = mul

* madd = add * m + a

注意:

        开ll,乘和加的优先级。

代码:
#include  #define ioscc ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)#define endl '\n'#define me(a, x) memset(a, x, sizeof a)#define all(a) a.begin(), a.end()#define sz(a) ((int)(a).size())#define pb(a) push_back(a)using namespace std; typedef unsigned long long ull;typedef long long ll;typedef pair pii;typedef vector> vvi;typedef vector vi;typedef vector vb; const int dx[4] = {-1, 0, 1, 0};const int dy[4] = {0, 1, 0, -1};const int MAX = (1ll ostream &operator &a) noexcept{    for (int i = 0; i istream &operator>>(istream &in, vector &a) noexcept{    for (int i = 0; i > a[i];    return in;} /* ----------------- 有乘就强转,前缀和开ll ----------------- */ int mod;ll v[N];struct Node{    int l, r;    ll sum;    ll add, mul;} tr[N > 1;    build(u = l && tr[u].r > 1;    ll sum = 0;    if (l  mid)        sum += query(u = l && tr[u].r > 1;    if (l  mid)        update(u > n >> m >> mod;     for (int i = 1; i > v[i];     build(1, 1, n);     while (m--)    {        int op;        int x, y, v;        cin >> op >> x >> y;        if (op == 1)        {            cin >> v;            update(1, x, y, v, 0);        }        else if (op == 2)        {            cin >> v;            update(1, x, y, 1, v);        }        else            cout << query(1, x, y) % mod << endl;    }} int main(){    ioscc;     solve();     return 0;}

(四)区间平方根问题:

1.P4145 上帝造题的七分钟 2 / 花神游历各国 - 洛谷
思路:

        由于维护平方根懒标记的方法不易实现,所以直接暴力对区间内的每个数开平方即可,a[i] <= 1e12,对于每个数最多计算6次其值就等于1,所以时间复杂度为O(6nlogn)。

注意:

        存在 l > r 的情况

代码:
#include  #define ioscc ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)#define endl '\n'#define me(a, x) memset(a, x, sizeof a)#define all(a) a.begin(), a.end()#define sz(a) ((int)(a).size())#define pb(a) push_back(a)using namespace std; typedef unsigned long long ull;typedef long long ll;typedef pair pii;typedef vector> vvi;typedef vector vi;typedef vector vb; const int dx[4] = {-1, 0, 1, 0};const int dy[4] = {0, 1, 0, -1};const int MAX = (1ll ostream &operator &a) noexcept{    for (int i = 0; i istream &operator>>(istream &in, vector &a) noexcept{    for (int i = 0; i > a[i];    return in;} /* ----------------- 有乘就强转,前缀和开ll ----------------- */ ull v[N];struct Node{    int l, r;    ull sum;    ull maxx;} tr[N > 1;    build(u = l && tr[u].r > 1;    ull sum = 0;    if (l  mid)        sum += query(u > 1;    if (x  mid)        update(u > n;    for (int i = 1; i > v[i];     build(1, 1, n);     cin >> m;    while (m--)    {        int op;        int l, r;        cin >> op >> l >> r;         if (l > r)            swap(l, r);         if (op == 0)            update(1, 1, n, l, r);        else            cout << query(1, l, r) << endl;    }} int main(){    ioscc;     solve();     return 0;}

(五)区间二进制问题:

1.P1558 色板游戏 - 洛谷
思路:

        二进制与维护。

注意:

        颜色是直接覆盖原颜色的,最大覆盖区间的mask和lazy直接赋值即可。

代码:
#include  #define ioscc ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)#define endl '\n'#define me(a, x) memset(a, x, sizeof a)#define all(a) a.begin(), a.end()#define sz(a) ((int)(a).size())#define pb(a) push_back(a)using namespace std; typedef unsigned long long ull;typedef long long ll;typedef pair pii;typedef vector> vvi;typedef vector vi;typedef vector vb; const int dx[4] = {-1, 0, 1, 0};const int dy[4] = {0, 1, 0, -1};const int MAX = (1ll ostream &operator &a) noexcept{    for (int i = 0; i istream &operator>>(istream &in, vector &a) noexcept{    for (int i = 0; i > a[i];    return in;} /* ----------------- 有乘就强转,前缀和开ll ----------------- */ struct Node{    int l, r;    ll mask;    ll lazy;} tr[N > 1;    build(u = l && tr[u].r > 1;    ll ans = 0;    if (l  mid)        ans |= query(u = l && tr[u].r > 1;    if (l  mid)        update(u > n >> m >> q;     build(1, 1, n);     while (q--)    {        char op;        ll a, b, c;        cin >> op >> a >> b;        if (a > b)            swap(a, b);        if (op == 'C')        {            cin >> c;            ll v = 1ll > i & 1);             cout << ans << endl;        }    }} int main(){    ioscc;     solve();     return 0;}

posted on 2025-07-18 19:35  ljbguanli  阅读(6)  评论(0)    收藏  举报