kuangbin带你飞 线段树 题解

本文详细介绍了线段树在解决区间查询和修改问题中的应用,包括单点修改、区间总和、区间最大值等操作。通过多个实例展示了线段树的构建、查询和修改过程,涉及区间加法、乘法、置为指定值等多种操作。同时,文章还讨论了如何处理懒惰标记和离散化等问题,以应对复杂场景。
摘要由CSDN通过智能技术生成

A-敌兵布阵
线段树的单点修改和查询区间总和操作

#include <iostream>

using namespace std;
const int N = 5e4 + 10;
struct Node
{
    int l, r;
    int sum;
}tr[4 * N];
int w[N];

void pushup(int u)
{
    tr[u].sum = tr[u << 1].sum +tr[u << 1 | 1].sum;
}

void build(int u, int l, int r)
{
    if (l == r)
    {
        tr[u] = {l, r, w[l]};
        return;
    }
    tr[u] = {l, r};
    int mid = l + r >> 1;
    build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
    pushup(u);
}

int query(int u, int l, int r)
{
    if (l <= tr[u].l && tr[u].r <= r)
        return tr[u].sum;
    int sum = 0;
    int mid = tr[u].l + tr[u].r >> 1;
    if (l <= mid)   sum += query(u << 1, l, r);
    if (r > mid)    sum += query(u << 1 | 1, l , r);
    return sum;
}

void modify(int u, int x, int v)
{
    if (tr[u].l == tr[u].r)
    {
        tr[u].sum += v;
        return;
    }
    int mid = tr[u].l + tr[u].r >> 1;
    if (x <= mid)   modify(u << 1, x, v);
    else    modify(u << 1 | 1, x, v);
    pushup(u);
}

int main()
{
    int T, n;
    cin >> T;
    for (int i = 1; i <= T; i ++)
    {
        printf("Case %d:\n", i);
        cin >> n;
        for (int i = 1; i <= n; i ++)
            scanf("%d", &w[i]);
        build(1, 1, n);
        char op[10];
        while(scanf("%s", op), op[0] != 'E')
        {
            if (op[0] == 'Q')
            {
                int x, y;
                scanf("%d%d", &x, &y);
                printf("%d\n", query(1, x, y));
            }
            if (op[0] == 'A')
            {
                int x, v;
                scanf("%d%d", &x, &v);
                modify(1, x, v);
            }
            if (op[0] == 'S')
            {
                int x, v;
                scanf("%d%d", &x, &v);
                modify(1, x, -v);
            }
        }
    }
    return 0;
}

B - I Hate It
线段树的单点修改和查询区间最大值操作

#include <iostream>

using namespace std;
int n, m;
const int N = 200000 + 10;

struct Node
{
    int l, r, v;
}tr[4 * N];
int w[N];

void pushup(int u)
{
    tr[u].v = max(tr[u << 1].v, tr[u << 1 | 1].v);
}

void build(int u, int l, int r)
{
    if (l == r)
    {
        tr[u] = {l, r, w[l]};
        return;
    }
    tr[u] = {l, r};
    int mid = l + r >> 1;
    build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
    pushup(u);
}

int query(int u, int l, int r)
{
    if (l <= tr[u].l && r >= tr[u].r)
    {
        return tr[u].v;
    }
    int mid = tr[u].l + tr[u].r >> 1;
    int res = -99999;
    if (l <= mid)   res = max(res, query(u << 1, l, r));
    if (r > mid)    res = max(res, query(u << 1 | 1, l, r));
    return res;
}

void modify(int u, int x, int v)
{
    if (tr[u].l == tr[u].r)
    {
        tr[u].v = v;
        return;
    }
    int mid = tr[u].l + tr[u].r >> 1;
    if (x <= mid)   modify(u << 1, x, v);
    if (x > mid)    modify(u << 1 | 1, x, v);
    pushup(u);
}

int main()
{
    while (cin >> n >> m)
    {
        for (int i = 1; i <= n; i ++)
            scanf("%d", &w[i]);
        build(1, 1, n);

        while(m --)
        {
            char op[3]; int x, y;
            scanf("%s%d%d", op, &x, &y);

            if (op[0] == 'Q')
                printf("%d\n", query(1, x, y));
            if (op[0] == 'U')
                modify(1, x, y);
        }
    }
    return 0;
}

C - A Simple Problem with Integers
线段树懒标记和查询区间和

#include <iostream>
#include <cstdio>

using namespace std;
const int N = 100000 + 10;
int n, m;
typedef long long LL;

int w[N];
struct Node
{
    int l, r;
    LL sum, add;
}tr[4 * N];

void pushup(int u)
{
    tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
}

void pushdown(int u)
{
    if (tr[u].add)
    {
        Node &root = tr[u];
        Node &l = tr[u << 1];
        Node &r = tr[u << 1 | 1];
        l.add += root.add, l.sum += (l.r - l.l + 1) * root.add;
        r.add += root.add, r.sum += (r.r - r.l + 1) * root.add;
        root.add = 0;
    }
}

void build(int u, int l, int r)
{
    if (l == r)
    {
        tr[u] = {l, r, w[l], 0};
        return;
    }
    tr[u] = {l, r};
    int mid = l + r >> 1;
    build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
    pushup(u);
}

LL query(int u, int l, int r)
{
    if (l <= tr[u].l && tr[u].r <= r)
        return tr[u].sum;
    pushdown(u);
    LL res = 0;
    int mid = tr[u].l + tr[u].r >> 1;
    if (l <= mid)   res += query(u << 1, l, r);
    if (r > mid)    res += query(u << 1 | 1, l, r);
    return res;
}

void modify(int u, int l, int r, int v)
{
    if (l <= tr[u].l && tr[u].r <= r)
    {
        tr[u].add += v;
        tr[u].sum += (LL)(tr[u].r - tr[u].l + 1) * v;
        return;
    }
    pushdown(u);
    int mid = tr[u].l + tr[u].r >> 1;
    if (l > mid)    modify(u << 1 | 1, l, r, v);
    else if (r <= mid)  modify(u << 1, l, r, v);
    else
    {
        modify(u << 1 | 1, l, r, v);
        modify(u << 1, l, r, v);
    }
    pushup(u);
}

int main()
{
    scanf("%d%d", &n, &m);

    for (int i = 1; i <= n; i ++ ) scanf("%d", &w[i]);

    build(1, 1, n);

    char op[2];
    int l, r, d;

    while (m -- )
    {
        scanf("%s%d%d", op, &l, &r);
        if (*op == 'C')
        {
            scanf("%d", &d);
            modify(1, l, r, d);
        }
        else printf("%lld\n", query(1, l, r));
    }

    return 0;
}

D - Mayor’s posters
懒标记+离散化
题目大意:对于每组测试数据,在墙上张贴海报。先读入海报的数量n,然后给出n个l,r表示海报的区间范围。如果两张海报区间有重合,则对于重合部分,第二张海报会覆盖第一张。问最后可以看见多少张海报。

首先对于墙的长度为10000000,直接做显然是不行的,因为n最大为10000,因此可以将海报的左右端点离散化。考虑到先后张贴的三张海报区间为为1,10。1,3。5,10。如果直接离散化,得到的答案为2,显然是错误的,因为对于4这个点,并没有加入离散化。因此,每次对于一个海报的区间l,r,将l,r,r + 1三个点加入离散化。

对于每次加入一个海报,每次用值不同的懒标记标记。最后遍历查询每一个,查询有多少个不同的点即可。

#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
#include <cstdio>

using namespace std;
const int N = 100010;
vector<int> xs;
int st[2 * N];
struct Segment
{
    int x1, x2;
}seg[N];

struct Node
{
    int l, r;
    int v, add;
}tr[8 * N];

void pushdown(int u)
{
    if (tr[u].add)
    {
        Node& root = tr[u], &left = tr[u << 1], &right = tr[u << 1 | 1];
        left.add = root.add, right.add = root.add;
        tr[u].add = 0;
    }
}

void build(int u, int l, int r)
{
    if (l == r)
    {
        tr[u] = {l, r, 0, 0};
        return;
    }
    tr[u] = {l, r, 0, 0};
    int mid = l + r >> 1;
    build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
}

void modify(int u, int l, int r, int v)
{
    if (l <= tr[u].l && tr[u].r <= r)
    {
        tr[u].add = v;
        return;
    }
    int mid = tr[u].l + tr[u].r >> 1;
    pushdown(u);
    if (l <= mid)   modify(u << 1, l, r, v);
    if (r > mid)    modify(u << 1 | 1, l, r, v);
}

int query(int u, int x)
{
    if (tr[u].l == tr[u].r)
    {
        return tr[u].add;
    }
    pushdown(u);
    int mid = tr[u].l + tr[u].r >> 1;
    if (x <= mid)   return query(u << 1, x);
    else    return query(u << 1 | 1, x);
}

int get(int x)
{
    return lower_bound(xs.begin(), xs.end(), x) - xs.begin();
}

int main()
{
    int T, n;
    cin >> T;
    while (T --)
    {
        memset(st, 0, sizeof st);
        int cnt = 1;
        scanf("%d", &n);
        for (int i = 0; i < n; i ++)
        {
            scanf("%d%d", &seg[i].x1, &seg[i].x2);
            xs.push_back(seg[i].x1), xs.push_back(seg[i].x2), xs.push_back(seg[i].x2 + 1);
        }
        sort(xs.begin(), xs.end());
        xs.erase(unique(xs.begin(), xs.end()), xs.end());

        build(1, 0, xs.size() - 1);
        for (int i = 0; i < n; i ++)
        {
            int x1 = get(seg[i].x1);
            int x2 = get(seg[i].x2);
            modify(1, x1, x2, cnt ++);
        }

        for (int i = 0; i < n; i ++)
        {
            int x1 = get(seg[i].x1);
            int x2 = get(seg[i].x2);
            int v = query(1, x1);
//            cout << v << endl;
            st[v] = 1;
            v = query(1, x2);
//            cout << v << endl;
            st[v] = 1;

            v = query(1, x2 + 1);
            st[v] = 1;
        }
        int len = xs.size();
        int res = 0;
        for (int i = 1; i <= 2 * len; i ++)
            if (st[i])  res ++;
        cout << res << endl;
    }
    return 0;
}

E - Just a Hook
G - Balanced Lineup
和A,B,C的思路相同,E是区间修改和查询总和。G是查询区间最大值和最小值。

H - Can you answer these queries?
题目大意:给定一个区间,将该区间的所有数都开根号。或者查询区间值的和。
首先,线段树结点存储区间的和,pushup操作为更新当前结点的和为左子节点的和+右子节点的和。
对于区间修改(开根号),我没有想到很好的方法。采用了每次遍历到每个叶节点进行修改操作然后pushup。为了防止TLE,因为值更新为1后再开根号没有意义,所以如果修改操作,向下递归时发现当前区间的所有数都是1,可以直接返回。(结点的和为区间长度)
区间查询为普通的区间查询。

#include <iostream>
#include <cstring>
#include <cmath>
#include <algorithm>

using namespace std;
typedef long long LL;
const int N = 1e5 + 10;
int n, m;
LL w[N];
struct Node
{
    int l, r;
    LL sum;
}tr[4 * N];

void pushup(int u)
{
    tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
}

void build(int u, int l, int r)
{
    if (l == r)
    {
        tr[u] = {l, r, w[l]};
        return;
    }
    int mid = l + r >> 1;
    tr[u] = {l ,r};
    build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
    pushup(u);
}

void modify(int u, int l, int r)
{
    if (tr[u].r - tr[u].l + 1 == tr[u].sum)
        return;
    if (tr[u].l == tr[u].r)
    {
        tr[u].sum = (LL)sqrt(1.0 * tr[u].sum);
        return;
    }
    int mid = tr[u].l + tr[u].r >> 1;
    if (l <= mid)
        modify(u << 1, l, r);
    if (r > mid)
        modify(u << 1 | 1, l, r);
    pushup(u);
}

LL query(int u, int l, int r)
{
    if (l <= tr[u].l &&tr[u].r <= r)
    {
        return tr[u].sum;
    }
    int mid = tr[u].l + tr[u].r >> 1;
    LL res = 0;
    if (l <= mid)
        res += query(u << 1, l, r);
    if (r > mid)
        res += query(u << 1 | 1, l, r);
    return res;
}

int main()
{
    int T = 1;
    while (scanf("%d", &n) != -1)
    {
        printf("Case #%d:\n", T ++);
        for (int i = 1; i <= n; i ++)
            scanf("%I64d", &w[i]);
        build(1, 1, n);
        scanf("%d", &m);

        int t, x, y;
        while (m --)
        {
            scanf("%d%d%d", &t, &x, &y);
            if (y < x)  swap(x, y);
            if (t == 0)
                modify(1, x, y);
            else
                printf("%I64d\n", query(1, x, y));
        }
        cout << endl;
    }
    return 0;
}

I - Tunnel Warfare
题目大致意思:抗日战争时期进行地道战,对于一系列点,敌人可以摧毁一个,我军也可以对上次摧毁的点进行修复。对于查询操作,查询包含这个点在内的最长的没有被摧毁序列的长度。

设如果某个点被摧毁,则值为0。否则值为1。
对于修复最近一次被摧毁的点,可以用栈来存储。
则问题变成了求包含某个点的最长1序列

线段树结点包含信息:左端点l,右端点r,该结点包含区间从左端点起从左到右的最长1序列长度lmax,该结点包含区间从右端点起从右到左的最长1序列长度rmax。

pushup操作即为(对于lmax来说):如果左子节点lmax小于左子节点区间长度,则父节点的lmax为左子节点 的lmax。否则表示左子节点lmax等于区间长度,即该左子节点区间全是1,则父节点的lmax为左子节点的整个区间+右子节点的lmax。

查询操作也是类似。比如:如果对于查询的这个点落在区间的中点左边,如果从该区间的中点至左的的连续1序列包含了查询点,则返回return tr[u << 1 | 1].lmax + tr[u << 1].rmax即可。否则递归查询左子结点。

#include <iostream>

using namespace std;
const int N = 5e4 + 10;

struct Node
{
    int l, r;
    int lmax, rmax;
}tr[4 * N];
int n, m;
int stk[N], top;
int x;

void pushup(int u)
{
    Node &root = tr[u], &left = tr[u << 1], &right = tr[u << 1 | 1];
    root.lmax = left.lmax;
    if (left.r - left.l + 1 == left.lmax)   root.lmax = left.lmax + right.lmax;

    root.rmax = right.rmax;
    if (right.r - right.l + 1 == right.rmax) root.rmax = right.rmax + left.rmax;
}

void build(int u, int l, int r)
{
    if (l == r)
    {
        tr[u] = {l, l, 1, 1};
        return;
    }
    tr[u] = {l, r};
    int mid = l + r >> 1;
    build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
    pushup(u);
}

void modify(int u, int x, int v)
{
    if (tr[u].l == tr[u].r)
    {
        tr[u].lmax = tr[u].rmax = v;
        return;
    }
    int mid = tr[u].l + tr[u].r >> 1;
    if (x <= mid)   modify(u << 1, x, v);
    else    modify(u << 1 | 1, x, v);
    pushup(u);
}

int query(int u, int x)
{
    if (tr[u].l == tr[u].r || tr[u].lmax == tr[u].r - tr[u].l + 1)
    {
        return tr[u].lmax;
    }

    int mid = tr[u].l + tr[u].r >> 1;
    // printf("mid: %d\n", mid);
    if(x <= mid)
    {
        // printf("l: %d r: %d\n", tr[u << 1 | 1].lmax, tr[u << 1].rmax);
        if (mid - x + 1 <= tr[u << 1].rmax)
        {
            return tr[u << 1 | 1].lmax + tr[u << 1].rmax;
        }
        else
        {
            return query(u << 1, x);
        }
    }
    if (x > mid)
    {
        if (x - mid <= tr[u << 1 | 1].lmax)
            return tr[u << 1].rmax + tr[u << 1 | 1].lmax;
        else return query(u << 1 | 1, x);
    }
}

int main()
{
    while (~scanf("%d %d",&n,&m))
{

    top = 0;
    build(1, 1, n);
    // for (int i = 1; i < N; i ++)
    // {
    //     if (tr[i].l != 0)
    //     {
    //         printf("l:%d r:%d %d %d\n", tr[i].l, tr[i].r, tr[i].lmax, tr[i].rmax);
    //     }
    // }

    char op[4]; int x;
    while (m --)
    {
        scanf("%s", op);
        if (op[0] == 'D')
        {
            scanf("%d", &x);
            stk[top ++] = x;
            modify(1, x, 0);
        }
        else if (op[0] == 'Q')
        {
            scanf("%d", &x);
            printf("%d\n", query(1, x));
        }
        else
        {
            if(x > 0)
            {
                x = stk[-- top];
                modify(1, x, 1);
            }
        }
            // for (int i = 1; i <= N; i ++)
            // if (tr[i].l == 3 && tr[i].r == 4)
            // {
            //     printf("tr.l,r : %d %d val: %d %d", tr[i].l, tr[i].r, tr[i].lmax, tr[i].rmax);
            //     puts("");
            // }
    }
}
    return 0;
}

J - Assign the task
主要难点是如何建立区间。可以采用dfs,根据时间戳来确定线段树。
其中s数组记录先序遍历的时间戳,f数组记录后序遍历的时间戳,可知子节点的时间戳长度一定被包含在父节点的时间戳长度内。因此修改父节点的区间[s[u], f[u]]可以将其所以子节点一并修改。

#include <iostream>
#include <cstdio>
#include <cstring>

using namespace std;
const int N = 5e4 + 10;

int st[N];
int s[N], f[N], dfn;
int h[N], e[N], ne[N], idx;

struct Node
{
    int l, r;
    int add, val;
}tr[4 * N];

void pushdown(int u)
{
    if (tr[u].add != -1)
    {
        Node &root = tr[u];
        Node &l = tr[u << 1];
        Node &r = tr[u << 1 | 1];
        l.add = root.add, l.val = root.add;
        r.add = root.add, r.val = root.add;
        root.add = -1;
    }
}

void build(int u, int l, int r)
{
    if (l == r)
    {
        tr[u] = {l, r, -1, -1};
        return;
    }
    tr[u] = {l, r, -1, -1};
    int mid = l + r >> 1;
    build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
}

int query(int u, int x)
{
    if (tr[u].l == tr[u].r)
        return tr[u].val;
    pushdown(u);

    int mid = tr[u].l + tr[u].r >> 1;
    
    if (x <= mid)   return query(u << 1, x);
    return query(u << 1 | 1, x);
}

void modify(int u, int l, int r, int v)
{
    if (l <= tr[u].l && tr[u].r <= r)
    {
        tr[u].add = v;
        tr[u].val = v;
        return;
    }
    pushdown(u);
    int mid = tr[u].l + tr[u].r >> 1;
    if (l <= mid)    modify(u << 1 , l, r, v);
    if (r > mid)  modify(u << 1 | 1, l, r, v);
}

void dfs(int u)
{
    s[u] = ++ dfn;
    for (int i = h[u]; ~i; i = ne[i])
    {
        int j = e[i];
        dfs(j);
    }
    f[u] = dfn;
}

void add(int a, int b)
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}

int main()
{
    int T;
    cin >> T;
    for (int t = 1; t <= T; t ++)
    {
        printf("Case #%d:\n", t);
        
        memset(h, -1, sizeof h);
        idx = 0, dfn = 0;
        memset(st, 0, sizeof st);
        
        int n, m, u, v, root;
        cin >> n;
        build(1, 1, n);
        for (int i = 0; i < n - 1; i ++)
        {
            cin >> u >> v;
            add(v, u), st[u] = 1;
        }
        
        for (int i = 1; i <= n; i ++)
            if (!st[i]) root = i;
        dfs(root);
        
        // cout << "起始和结束:";
        // for (int i = 1; i <= n; i ++)
        //     cout << i << ' ' << s[i] << ' ' << f[i] << endl;
        // cout << endl;
        
        cin >> m;
        while(m --)
        {
            // for (int i = 1; i <= n; i ++)
            //     cout << query(1, i) << ' ';
            // cout << endl;
            
            char op[5];
            scanf("%s", op);
            if (op[0] == 'C')
            {
                int x;
                cin >> x;
                cout << query(1, s[x]) << endl;
            }
            else
            {
                int x, y;
                cin >> x >> y;
                modify(1, s[x], f[x], y);
            }
        }
    }
}

K - Transformation
对某段区间执行四种操作之一

  1. 都+c
  2. 都*c
  3. 都置为c
  4. 查询每个数字p次方的和

对于线段树的结点,懒标记add表示+操作,mul表示*操作,val表示置为某个数的操作。对于某个数,设其当前值为v * mul + add(当然刚刚建立完树时,mul为1,add为0),当传来乘法标记时,设其值为a,则该数变为(v * mul + add) * a,即add = add * a, mul = mul * a。当加法标记传来,设值为a,则mul不变,add = add + a。置值的标记传来,add变为0,mul变为1,即变成一开始的样子,val变成a。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

typedef long long LL;

const int N = 100010;
const int mod = 10007;

int n, m;
int w[N];
struct Node
{
    int l, r;
    long long sum[3];
    int add, mul, val;
}tr[N * 4];

void pushup(int u)
{
    tr[u].sum[0] = (tr[u << 1].sum[0] + tr[u << 1 | 1].sum[0]) % mod;
    tr[u].sum[1] = (tr[u << 1].sum[1] + tr[u << 1 | 1].sum[1]) % mod;
    tr[u].sum[2] = (tr[u << 1].sum[2] + tr[u << 1 | 1].sum[2]) % mod;
}

void eval(Node &t, int add, int mul, int val)
{
    LL len = t.r - t.l + 1;
    int a = mul;
    int b = add;
    if (val)
    {
        t.sum[0] = (LL)val % mod * len % mod;
        t.sum[1] = (LL)val % mod * val % mod * len % mod;
        t.sum[2] = (LL)val % mod * val % mod * val % mod * len % mod;
        t.add = 0, t.mul = 1, t.val = val;
    }
        t.sum[2] = ((LL)a * a * a % mod * t.sum[2] % mod + 3 * (LL)a * a * b % mod * t.sum[1]
                   + 3 * (LL)a * b * b % mod * t.sum[0] % mod + (LL)b * b * b % mod * len % mod) % mod;
        t.sum[1] = ((LL)a * a * t.sum[1] % mod + 2 * (LL)a * b % mod * t.sum[0] % mod + (LL)b * b % mod * len % mod) % mod;
        t.sum[0] = ((LL)t.sum[0] * mul + (LL)(t.r - t.l + 1) * add) % mod;
        
        t.mul = (LL)t.mul * mul % mod;
        t.add = ((LL)t.add * mul % mod + add) % mod;
    
}

void pushdown(int u)
{
    eval(tr[u << 1], tr[u].add, tr[u].mul, tr[u].val);
    eval(tr[u << 1 | 1], tr[u].add, tr[u].mul, tr[u].val);
    tr[u].add = 0, tr[u].mul = 1, tr[u].val = 0;
}

void build(int u, int l, int r)
{
    if (l == r) tr[u] = {l, r, 0, 0, 0, 0, 1, 0};
    else
    {
        tr[u] = {l, r, 0, 0, 0, 0, 1, 0};
        int mid = l + r >> 1;
        build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
        pushup(u);
    }
}

void modify(int u, int l, int r, int add, int mul, int val)
{
    if (tr[u].l >= l && tr[u].r <= r) eval(tr[u], add, mul, val);
    else
    {
        pushdown(u);
        int mid = tr[u].l + tr[u].r >> 1;
        if (l <= mid) modify(u << 1, l, r, add, mul, val);
        if (r > mid) modify(u << 1 | 1, l, r, add, mul, val);
        pushup(u);
    }
}

int query(int u, int l, int r, int p)
{
    if (tr[u].l >= l && tr[u].r <= r) return tr[u].sum[p];

    pushdown(u);
    int mid = tr[u].l + tr[u].r >> 1;
    int sum = 0;
    if (l <= mid) sum = query(u << 1, l, r, p);
    if (r > mid) sum = (sum + query(u << 1 | 1, l, r, p)) % mod;
    return sum;
}

int main()
{
    
    while (scanf("%d%d", &n, &m), n || m)
    {
        build(1, 1, n);
    
        int t, x, y, c;
        while (m -- )
        {
           scanf("%d%d%d%d", &t, &x, &y, &c);
           if (t == 1)  modify(1, x, y, c, 1, 0);
           if (t == 2)  modify(1, x, y, 0, c, 0);
           if (t == 3)  modify(1, x, y, 0, 1, c);
           if (t == 4)  printf("%d\n", query(1, x, y, c - 1) % mod);
        }
    }
    return 0;
}

L - Vases and Flowers
线段树+二分,难度不大

约会 安排

#include <iostream>
#include <cstring>

using namespace std;
const int N = 1e5 + 10;

struct Node
{
    int l, r;
    int almax, atmax, armax;  //屌丝
    int blmax, btmax, brmax;  //女神
    int add;
}tr[4 * N];

void build(int u, int l, int r)
{
    if (l == r)
    {
        tr[u] = {l, r, 1, 1, 1, 1, 1, 1, 0};
        return;
    }
    int mid = l + r >> 1;
    int len = r - l + 1;
    tr[u] = {l, r, len, len, len, len, len, len, 0};
    build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
}

void fun(int u, int v)
{
    tr[u].add = v;
    if (v == 3)
    {
        tr[u].almax = tr[u].atmax = tr[u].armax = tr[u].r - tr[u].l + 1;
        tr[u].blmax = tr[u].btmax = tr[u].brmax = tr[u].r - tr[u].l + 1;
    }
    if (v == 1)
    {
        tr[u].almax = tr[u].atmax = tr[u].armax = 0;
        tr[u].blmax = tr[u].btmax = tr[u].brmax = tr[u].r - tr[u].l + 1;
    }
    if (v == 2)
    {
        tr[u].almax = tr[u].atmax = tr[u].armax = 0;
        tr[u].blmax = tr[u].btmax = tr[u].brmax = 0;
    }
}

void pushdown(int u)
{
    if (tr[u].add)
    {
        fun(u << 1, tr[u].add), fun(u << 1 | 1, tr[u].add);
        tr[u].add = 0;
    }
}

void pushup(int u)
{
    Node &root = tr[u], &left = tr[u << 1], &right = tr[u << 1 | 1];
    tr[u].almax = tr[u << 1].almax;
    if (tr[u << 1].almax == left.r - left.l + 1)
        tr[u].almax = left.r - left.l + 1 + tr[u << 1 | 1].almax;

    tr[u].armax = tr[u << 1 | 1].armax;
    if (tr[u << 1 | 1].armax == right.r - right.l + 1)
        tr[u].armax = right.r - right.l + 1 + left.armax;

    root.atmax = max(max(left.atmax, right.atmax), left.armax + right.almax);

    tr[u].blmax = tr[u << 1].blmax;
    if (tr[u << 1].blmax == left.r - left.l + 1)
        tr[u].blmax = left.r - left.l + 1 + tr[u << 1 | 1].blmax;

    tr[u].brmax = tr[u << 1 | 1].brmax;
    if (tr[u << 1 | 1].brmax == right.r - right.l + 1)
        tr[u].brmax = right.r - right.l + 1 + left.brmax;

    root.btmax = max(max(left.btmax, right.btmax), left.brmax + right.blmax);
}

void modify(int u, int l, int r, int v)
{
    if (l <= tr[u].l && tr[u].r <= r)
    {
        fun(u, v);
        return;
    }
    pushdown(u);
    int mid = tr[u].l + tr[u].r >> 1;
    if (l <= mid)   modify(u << 1, l, r, v);
    if (r > mid)    modify(u << 1 | 1, l, r, v);
    pushup(u);
}

int query(int u, int x, int flag)
{
    if (tr[u].l == tr[u].r)
    {
        return tr[u].l;
    }

    pushdown(u);
    if (flag == 1)
    {
        if (tr[u << 1].atmax >= x)
            return query(u << 1, x, 1);
        else if (tr[u << 1].armax + tr[u << 1 | 1].almax >= x)
            return tr[u << 1].r - tr[u << 1].armax + 1;
        else    return query(u << 1 | 1, x, 1);
    }
    if (flag == 2)
    {
        if (tr[u << 1].btmax >= x)
            return query(u << 1, x, 2);
        else if (tr[u << 1].brmax + tr[u << 1 | 1].blmax >= x)
           return tr[u << 1].r - tr[u << 1].brmax + 1;
        else    return query(u << 1 | 1, x, 2);
    }
}

int main()
{
    int num;
    int cas = 1;
    cin >> num;
    while (num --)
    {
        printf("Case %d:\n", cas ++);
        int t, m;
        scanf("%d%d", &t, &m);
        build(1, 1, t);

        char op[10];
        int x, y;
        while (m --)
        {
            scanf("%s", op);
            if (op[0] == 'D')
            {
                scanf("%d", &x);
                if (tr[1].atmax < x)
                    printf("fly with yourself\n");
                else
                {
                    int last = query(1, x, 1);     //query(u, 长度,类型)  1表示屌丝
                    modify(1, last, x + last - 1, 1);
                    printf("%d,let's fly\n", last);
                }
            }
            else if (op[0] == 'N')  //女神
            {
                scanf("%d", &x);
                if (tr[1].btmax < x)
                    printf("wait for me\n");
                else
                {
                    if (tr[1].atmax < x)
                    {
                        int last = query(1, x, 2);     //query(u, 长度,类型)  2表示女生
                        modify(1, last, x + last - 1, 2);
                        printf("%d,don't put my gezi\n", last);
                    }
                    else
                    {
                        int last = query(1, x, 1);
                        modify(1, last, x + last - 1, 2);
                        printf("%d,don't put my gezi\n", last);
                    }
                }
            }
            else
            {
                scanf("%d%d", &x, &y);
                modify(1, x, y, 3);                //3表示清0
                printf("I am the hope of chinese chengxuyuan!!\n");
            }
        }
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值