线段树水题几枚

最近接触了一下线段树,按理来说这个半年前就该看了,实际上自己却总想刷水题而躲避难题,不过,该来的还是要来的,既然选择了数据结构,就让各种树来的猛烈些吧。


以下为最初级的线段树,只更新点,没有delay操作


1.HDU 1166 敌兵布阵

这道题用线段树或者树状数组都可以做,HDU上的数据貌似很弱,模拟竟然都能过

首先是线段树版 ,线段树所带信息为当前区间所有点的值的和

/*
ID: sdj22251
PROG: subset
LANG: C++
*/
#include <iostream>
#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <string>
#include <cstring>
#include <cmath>
#include <ctime>
#define LOCA
#define MAXN 50005
#define INF 100000000
#define eps 1e-7
#define L(x) x<<1
#define R(x) x<<1|1
using namespace std;
int a[MAXN];
struct node
{
    int left, right, mid;
    int sum;
}tree[4 * MAXN];
int ans;
void up(int C)
{
    tree[C].sum = tree[L(C)].sum + tree[R(C)].sum;
}
void make_tree(int s, int e, int C)
{
    tree[C].left = s;
    tree[C].right = e;
    tree[C].mid = (s + e) / 2;
    tree[C].sum = 0;
    if(s == e)
    {
        tree[C].sum = a[s];
        return;
    }
    make_tree(s, tree[C].mid, L(C));
    make_tree(tree[C].mid + 1, e, R(C));
    up(C);
}
void update(int C, int p, int v)
{
    if(tree[C].left == p && tree[C].right == p)
    {
        tree[C].sum += v;
        return;
    }
    if(p <= tree[C].mid)
    update(L(C), p, v);
    else update(R(C), p, v);
    up(C);
}
void search(int s, int e, int C)
{
    if(s <= tree[C].left && tree[C].right <= e)
    {
        ans += tree[C].sum;
        return;
    }
    if(tree[C].mid >= e) search(s, e, L(C));
    else if(tree[C].mid < s) search(s, e, R(C));
    else
    {
        search(s, tree[C].mid, L(C));
        search(tree[C].mid + 1, e, R(C));
    }
}
int main()
{
#ifdef LOCAL
    freopen("d:/data.in","r",stdin);
    freopen("d:/data.out","w",stdout);
#endif
    char s[20];
    int t, n, i, cas = 0, x, y;
    scanf("%d", &t);
    while(t--)
    {
        scanf("%d", &n);
        for(i = 1; i <= n; i++)
        scanf("%d", &a[i]);
        make_tree(1, n, 1);
        printf("Case %d:\n", ++cas);
        while(scanf("%s", s) != EOF)
        {
            if(s[0] == 'E') break;
            else if(s[0] == 'Q')
            {
                ans = 0;
                scanf("%d%d", &x, &y);
                search(x, y, 1);
                printf("%d\n", ans);
            }
            else if(s[0] == 'A')
            {
                scanf("%d%d", &x, &y);
                update(1, x, y);
            }
            else if(s[0] == 'S')
            {
                scanf("%d%d", &x, &y);
                update(1, x, -y);
            }
        }
    }
    return 0;
}



然后是树状数组版


/*
ID: sdj22251
PROG: lamps
LANG: C++
*/
#include <iostream>
#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <string>
#include <cstring>
#include <cmath>
#include <ctime>
#define MAX 2000000000
#define LOCA
using namespace std;
int a[50005], n, b[50005];
int lowbit(int x)
{
    return x & -x;
}
void modify(int x)
{
    for(int i = x; i <= n; i += lowbit(i))
    a[i] += b[x];
}
void modify2(int x, int y)
{
    for(int i = x; i <= n; i += lowbit(i))
    a[i] += y;
}
int sum(int x)
{
    int ans = 0;
    for(int i = x; i >= 1; i -= lowbit(i))
    ans += a[i];
    return ans;
}
int main()
{
#ifdef LOCAL
    freopen("lamps.in","r",stdin);
    freopen("lamps.out","w",stdout);
#endif
    int t, cas = 0, i, x, y;
    char s[15];
    scanf("%d", &t);
    while(t--)
    {
        memset(a, 0, sizeof(a));
        memset(b, 0, sizeof(b));
        scanf("%d", &n);
        for(i = 1; i <= n; i++)
        {
            scanf("%d", &b[i]);
            modify(i);
        }
        printf("Case %d:\n", ++cas);
        while(scanf("%s", s) != EOF)
        {
            if(s[0] == 'E')
            break;
            else if(s[0] == 'Q')
            {
                scanf("%d%d", &x, &y);
                printf("%d\n", sum(y) - sum(x - 1));
            }
            else if(s[0] == 'A')
            {
                scanf("%d%d", &x, &y);
                modify2(x, y);
            }
            else if(s[0] == 'S')
            {
                scanf("%d%d", &x, &y);
                modify2(x, -y);
            }
        }
    }
    return 0;
}



2.HDU 1754 I Hate It

同样是点更新,外加up操作即可,线段树所带信息是当前区间的最大值

/*
ID: sdj22251
PROG: subset
LANG: C++
*/
#include <iostream>
#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <string>
#include <cstring>
#include <cmath>
#include <ctime>
#define LOCA
#define MAXN 200005
#define INF 100000000
#define eps 1e-7
using namespace std;
struct node
{
    int left, right, mid;
    int l, mx;
} tree[4 * MAXN];
int ans, num[MAXN];
void make_tree(int s, int e, int C)
{
    tree[C].left = s;
    tree[C].right = e;
    tree[C].mid = ((s + e) >> 1);
    tree[C].l = tree[C].right - tree[C].left + 1;
    tree[C].mx = 0;
    if(s == e) {tree[C].mx = num[s]; return;}
    make_tree(s, tree[C].mid, (C << 1));
    make_tree(tree[C].mid + 1, e, (C << 1) | 1);
    tree[C].mx = max(tree[C << 1].mx, tree[(C << 1) + 1].mx);
}
void update(int s, int v, int C)
{
    if(tree[C].left == s && tree[C].right == s)
    {
        tree[C].mx = v;
        return;
    }
    if(s > tree[C].mid)
    {
        update(s, v, (C << 1) | 1);
    }
    else update(s, v, C << 1);
    tree[C].mx = max(tree[C << 1].mx, tree[(C << 1) + 1].mx);
}
void search(int s, int e, int C)
{
    if(tree[C].left >= s && tree[C].right <= e)
    {
        if(tree[C].mx > ans)
        ans = tree[C].mx;
        return;
    }
    if(s > tree[C].mid)
    {
        search(s, e, (C << 1) | 1);
    }
    else if(e <= tree[C].mid)
    {
        search(s, e, C << 1);
    }
    else
    {
        search(s, tree[C].mid, C << 1);
        search(tree[C].mid + 1, e, (C << 1) | 1);
    }
}
int main()
{
#ifdef LOCAL
    freopen("d:/data.in","r",stdin);
    freopen("d:/data.out","w",stdout);
#endif
    int n, m, i, x, y;
    char s[5];
    while(scanf("%d%d", &n, &m) != EOF)
    {
        for(i = 1; i <= n; i++)
        scanf("%d", &num[i]);
        make_tree(1, n, 1);
        while(m--)
        {
            scanf("%s %d %d", s, &x, &y);
            if(s[0] == 'Q')
            {
                ans = 0;
                search(x, y, 1);
                printf("%d\n", ans);
            }
            else
            {
                update(x, y, 1);
            }
        }
    }
    return 0;
}

3.HDU 1394Minimum Inversion Number

题意就是求逆序数,然后每次把当前序列的第一个数放到序列末尾求一次逆序数,总共n次,求其中最小的逆序数

其实求出最初的逆序数就行了,因为随后的逆序数能推导出来,总共n个数,0到n-1都有,比a[i]小的数是a[i] 个,大的数是n-1-a[i]个。

求逆序数时所用的方法是先查询再更新,查询的是已经存在的比自己大的数的个数,而线段树中所带的信息就是这一区间有几个数存在。

/*
ID: sdj22251
PROG: subset
LANG: C++
*/
#include <iostream>
#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <string>
#include <cstring>
#include <cmath>
#include <ctime>
#define LOCA
#define MAXN 50005
#define INF 100000000
#define eps 1e-7
#define L(x) x<<1
#define R(x) x<<1|1
using namespace std;
struct node
{
    int left, right, mid;
    int sum;
}tree[4 * MAXN];
int tmp, a[MAXN];
void up(int C)
{
    tree[C].sum = tree[L(C)].sum + tree[R(C)].sum;
}
void make_tree(int s, int e, int C)
{
    tree[C].left = s;
    tree[C].right = e;
    tree[C].mid = (s + e) / 2;
    tree[C].sum = 0;
    if(s == e) return;
    make_tree(s, tree[C].mid, L(C));
    make_tree(tree[C].mid + 1, e, R(C));
}
void search(int s, int e, int C)
{
    if(s <= tree[C].left && e >= tree[C].right)
    {
        tmp += tree[C].sum;
        return;
    }
    if(tree[C].mid >= e) search(s, e, L(C));
    else if(tree[C].mid < s) search(s, e, R(C));
    else
    {
        search(s, tree[C].mid, L(C));
        search(tree[C].mid + 1, e, R(C));
    }
}
void update(int p, int s, int e, int C)
{
    if(s == e && s == p) {tree[C].sum++; return;}
    if(tree[C].mid >= p) update(p, s, tree[C].mid, L(C));
    else update(p, tree[C].mid + 1, e, R(C));
    up(C);
}
int main()
{
#ifdef LOCAL
    freopen("d:/data.in","r",stdin);
    freopen("d:/data.out","w",stdout);
#endif
    int n, i;
    while(scanf("%d", &n) != EOF)
    {
        make_tree(0, n - 1, 1);
        int sum = 0;
        for(i = 0; i < n; i++)
        {
            scanf("%d", &a[i]);
            tmp = 0;
            search(a[i], n - 1, 1);
            sum += tmp;
            update(a[i], 0, n - 1, 1);
        }
        int ret = sum;
        for(i = 0; i < n; i++)
        {
            sum = sum + n - a[i] - a[i] - 1;
            ret = min(ret, sum);
        }
        printf("%d\n", ret);
    }
    return 0;
}


4.HDU 2795 Billboard

题意是给出一个 高为h宽为w的木板,然后往上贴高度为1,宽度wi的纸条,每次帖优先找到最高的帖,高度相同的要尽量往左边贴。

其实把板子横过来的话就好理解了,横坐标是h,然后尽量往左边贴条子

数据范围貌似挺大的,其实也不大,因为n只有20万,也就是说线段树的范围1到20万够用了

然后线段树的附加信息是这个区间内每一段h的剩余空间的最大值,查询的时候最终查到叶子节点,减掉消耗的长度就行了。

/*
ID: sdj22251
PROG: subset
LANG: C++
*/
#include <iostream>
#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <sstream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cctype>
#include <string>
#include <cstring>
#include <cmath>
#include <ctime>
#define LOCA
#define MAXN 200005
#define INF 100000000
#define eps 1e-7
#define L(x) x<<1
#define R(x) x<<1|1
using namespace std;
int h, w, n;
struct node
{
    int left, right, mid;
    int mx;
}tree[4 * MAXN];
void make_tree(int s, int e, int C)
{
    tree[C].left = s;
    tree[C].right = e;
    tree[C].mid = (s + e) / 2;
    tree[C].mx = w;
    if(s == e) return;
    make_tree(s, tree[C].mid, L(C));
    make_tree(tree[C].mid + 1, e, R(C));
}
int insert(int C, int v)
{
    if(tree[C].mx < v) return -1;
    if(tree[C].left == tree[C].right)
    {
        tree[C].mx -= v;
        return tree[C].left;
    }
    int ans = -1;
    if(v <= tree[L(C)].mx)
    ans = insert(L(C), v);
    else if(v <= tree[R(C)].mx)
    ans = insert(R(C), v);
    tree[C].mx = max(tree[L(C)].mx, tree[R(C)].mx);
    return ans;
}
int main()
{
#ifdef LOCAL
    freopen("d:/data.in","r",stdin);
    freopen("d:/data.out","w",stdout);
#endif
    int i, x;
    while(scanf("%d%d%d", &h, &w, &n) != EOF)
    {
        if(h > n) h = n;
        make_tree(1, h, 1);
        for(i = 0; i < n; i++)
        {
            scanf("%d", &x);
            printf("%d\n", insert(1, x));
        }
    }
    return 0;
}


然后做了这几个初级题目后,发现查询的时候,一种是需要自定义查询范围的,还有一种是不需要自定义查询范围, 直接从祖先开始查的,查询函数的参数的个数自然就不同了


### 关于线段树的第六道练习 对于线段树的应用,在多个平台和资料中有丰富的练习供学习者挑战。具体到第六道练习的选择,这取决于具体的课程设置或在线评测系统的安排[^1]。 通常情况下,线段树相关的练习会逐步增加难度,从基础操作如单点更新、区间查询开始,逐渐过渡到更复杂的懒惰传播(Lazy Propagation)、多维线段树等问上。考虑到这一点,假设在一个典型的训练序列中,“智乃酱的平方数列”可能作为一道中级偏上的目出现,该不仅涉及到了基本的线段树构建维护,还加入了对等差数列以及多项式的处理[^2]。 为了更好地理解这类问并找到特定编号的目,建议访问专门提供此类资源的网站,比如牛客网或其他知名编程竞赛平台。这些平台上往往会有详细的分类标签帮助定位目标目。例如,在牛客网上可以通过搜索关键词“线段树”,然后按照推荐顺序浏览直到找到所需的第六[^3]。 另外值得注意的是,不同平台之间的目编排可能存在差异,因此所谓的“第六”并不是绝对固定的,而是相对而言的一个位置概念。如果希望获得最准确的结果,可以直接查阅所使用的教材或者在线课程的具体章节来确定哪一被指定为第六[^4]。 ```cpp // 下面是一个简单的线段树节点定义示例,用于解决某些类型的区间查询问 struct Node { int l, r; long long sum; // 区间总和 long long sum2; // 区间每个数字的平方和 long long add; // 加法懒标记 long long mul; // 乘法懒标记 }; ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值