2022GDUT寒假专题学习-5 树状数组,线段树

2022/2/22 6:23:42

本文主要是介绍2022GDUT寒假专题学习-5 树状数组,线段树,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

前言

专题链接:GDUT-21级第五次专题训练——树状数组,线段树 - Virtual Judge (vjudge.net)

本专题内容都是树状数组和线段树的简单应用。在刚接触的时候,树状数组因为其代码量比线段树少很多,所以比较好入手,但因为树状数组其实是一种利用二进制的结构,所以深入理解起来的话其实比线段树难。线段树就是代码量多但很好理解。

线段树模板2

题目

如题,已知一个数列,你需要进行下面三种操作:

  • 将某区间每一个数乘上 xx
  • 将某区间每一个数加上 xx
  • 求出某区间每一个数的和

image-20220221200223664

image-20220221200249821

思想

是同时带有加法和乘法运算的线段树模板题。

因为会同时出现加法和乘法,那么问题就是如何正确处理 lazy tag 和 pushdown 的加法和乘法的运算先后关系。

一般为了方便和避免出现精度丢失问题,我们会采用先乘后加的方法。

在同时维护两个 lazy tag (multag 和 addtag)时,因为每次都是先乘后加,因此每次操作的加法都不会对乘法产生影响,所以 multag 只需要乘上要乘的数就行了;而因为每乘上一个数,都会同时影响到之前已经加过的数,所以 addtag 需要先乘一个乘数再加上要加的数。

inline void f(ll p, ll l, ll r, ll add, ll mul) {
    tr[p] = (tr[p] * mul % mod + add * (r - l + 1) % mod) % mod;  //先乘后加
    multag[p] = multag[p] * mul % mod;
    addtag[p] = (addtag[p] * mul % mod + add) % mod;
}
inline void pushdown(ll p, ll l, ll r) {
    ll mid = (l + r) >> 1;
    f(ls(p), l, mid, addtag[p], multag[p]);
    f(rs(p), mid + 1, r, addtag[p], multag[p]);
    addtag[p] = 0;
    multag[p] = 1;
}

注意

1、addtag的初始化状态为0,multag的初始化状态为1

代码

#include <algorithm>
#include <cstring>
#include <iostream>
#include <queue>
#include <vector>
#define SF ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
using namespace std;
typedef long long ll;
typedef pair<ll, ll> P;
const int inf = 0x3f3f3f3f;
const int N = 1e5 + 10;
ll n, m, mod;
ll a[N], tr[N << 2], addtag[N << 2], multag[N << 2];
inline ll ls(ll x) { return x << 1; }
inline ll rs(ll x) { return x << 1 | 1; }
inline void pushup(ll p) {
    tr[p] = (tr[ls(p)] + tr[rs(p)]) % mod;
}
inline void bt(ll p, ll l, ll r) {
    multag[p] = 1;
    if (l == r) {
        tr[p] = a[l] % mod;
        return;
    }
    ll mid = (l + r) >> 1;
    bt(ls(p), l, mid);
    bt(rs(p), mid + 1, r);
    pushup(p);
}
inline void f(ll p, ll l, ll r, ll add, ll mul) {
    tr[p] = (tr[p] * mul % mod + add * (r - l + 1) % mod) % mod;
    multag[p] = multag[p] * mul % mod;
    addtag[p] = (addtag[p] * mul % mod + add) % mod;
}
inline void pushdown(ll p, ll l, ll r) {
    ll mid = (l + r) >> 1;
    f(ls(p), l, mid, addtag[p], multag[p]);
    f(rs(p), mid + 1, r, addtag[p], multag[p]);
    addtag[p] = 0;
    multag[p] = 1;
}
inline void update(ll dl, ll dr, ll p, ll l, ll r, ll add, ll mul) {
    if (dl <= l && r <= dr) {
        tr[p] = (tr[p] * mul % mod + add * (r - l + 1) % mod) % mod;
        multag[p] = multag[p] * mul % mod;
        addtag[p] = (addtag[p] * mul % mod + add) % mod;
        return;
    }
    pushdown(p, l, r);
    ll mid = (l + r) >> 1;
    if (dl <= mid) update(dl, dr, ls(p), l, mid, add, mul);
    if (mid < dr) update(dl, dr, rs(p), mid + 1, r, add, mul);
    pushup(p);
}
inline ll query(ll ql, ll qr, ll p, ll l, ll r) {
    ll sum = 0;
    if (ql <= l && r <= qr) {
        return tr[p];
    }
    pushdown(p, l, r);
    ll mid = (l + r) >> 1;
    if (ql <= mid) sum += query(ql, qr, ls(p), l, mid), sum %= mod;
    if (mid < qr) sum += query(ql, qr, rs(p), mid + 1, r), sum %= mod;
    return sum % mod;
}
int main() {
    SF;
    cin >> n >> m >> mod;
    for (int i = 1; i <= n; ++i) {
        cin >> a[i];
    }
    bt(1, 1, n);
    while (m--) {
        ll o, x, y, k;
        cin >> o;
        if (o == 1) {
            cin >> x >> y >> k;
            update(x, y, 1, 1, n, 0, k);
        } else if (o == 2) {
            cin >> x >> y >> k;
            update(x, y, 1, 1, n, k, 1);
        } else {
            cin >> x >> y;
            cout << query(x, y, 1, 1, n) << endl;
        }
    }
    return 0;
}

E - Lost Cows

题目

N (2 <= N <= 8,000) cows have unique brands in the range 1..N. In a spectacular display of poor judgment, they visited the neighborhood 'watering hole' and drank a few too many beers before dinner. When it was time to line up for their evening meal, they did not line up in the required ascending numerical order of their brands.

Regrettably, FJ does not have a way to sort them. Furthermore, he's not very good at observing problems. Instead of writing down each cow's brand, he determined a rather silly statistic: For each cow in line, he knows the number of cows that precede that cow in line that do, in fact, have smaller brands than that cow.

Given this data, tell FJ the exact ordering of the cows.

image-20220221231838675

image-20220221231901303

思想

从后往前枚举,用二分查找的方法依次给每头牛选择标签

先让每一个标签都处于未被选择状态 add(i,1);

在判断最后一头牛时,因为标签一个都没有被选过,所以此时这头牛的标签就是 在它前面比标签比它大的牛的数量+1,然后选择该标签add(pos,-1);,剩下的以此类推。

代码

#include <algorithm>
#include <cstring>
#include <iostream>
#include <queue>
#include <vector>
#define SF ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
using namespace std;
typedef long long ll;
typedef pair<ll, ll> P;
const int inf = 0x3f3f3f3f;
ll a[10000], c[10000], ans[10000];
ll n;
ll lowbit(ll x) { return x & -x; }
void add(ll u, ll v) {
    for (; u <= n; u += lowbit(u)) c[u] += v;
}
ll sum(ll u) {
    ll ans = 0;
    for (; u > 0; u -= lowbit(u)) ans += c[u];
    return ans;
}
ll erf(ll x) {
    ll l = 1, r = n, pos;
    while (l < r) {
        pos = (l + r) / 2;
        if (sum(pos) >= x) {
            r = pos;
        } else if (sum(pos) < x) {
            l = pos + 1;
        }
    }
    return r;
}
int main() {
    SF;
    cin >> n;
    for (int i = 2; i <= n; ++i) cin >> a[i];
    for (int i = 1; i <= n; ++i) add(i, 1);
    for (int i = n; i >= 1; --i) {
        ll h = erf(a[i] + 1);
        add(h, -1);
        ans[i] = h;
    }
    for (int i = 1; i <= n; ++i) cout << ans[i] << endl;
    return 0;
}

G - Mayor's posters

题目

The citizens of Bytetown, AB, could not stand that the candidates in the mayoral election campaign have been placing their electoral posters at all places at their whim. The city council has finally decided to build an electoral wall for placing the posters and introduce the following rules:

  • Every candidate can place exactly one poster on the wall.
  • All posters are of the same height equal to the height of the wall; the width of a poster can be any integer number of bytes (byte is the unit of length in Bytetown).
  • The wall is divided into segments and the width of each segment is one byte.
  • Each poster must completely cover a contiguous number of wall segments.

They have built a wall 10000000 bytes long (such that there is enough place for all candidates). When the electoral campaign was restarted, the candidates were placing their posters on the wall and their posters differed widely in width. Moreover, the candidates started placing their posters on wall segments already occupied by other posters. Everyone in Bytetown was curious whose posters will be visible (entirely or in part) on the last day before elections.
Your task is to find the number of visible posters when all the posters are placed given the information about posters' size, their place and order of placement on the electoral wall.

image-20220222004634695

image-20220222004655175

思想

乍一看就是常规的线段树做法,但仔细一看发现,数据范围太大了,直接做的话会MLE,需要优化一下数据范围。

一看1<=n<=1e4,而且题目要求的是最后可见海报的数量,和海报本身的长度没有关系,于是乎,我们可以使用离散化来优化数据范围。

代码

#include <algorithm>
#include <cstring>
#include <iostream>
#include <queue>
#include <vector>
#define SF ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define ls p << 1
#define rs p << 1 | 1
using namespace std;
typedef long long ll;
typedef pair<int, int> P;
const int inf = 0x3f3f3f3f;
const int N = 1e5 + 3;
int tr[N * 10], vis[N], l[N], r[N], ans;
void pushup(int p) {
    if (tr[ls] != tr[rs])
        tr[p] = 0;
    else
        tr[p] = tr[ls];
}
void bt(int p, int l, int r) {
    if (l == r) {
        tr[p] = 0;
        return;
    }
    int mid = (l + r) >> 1;
    bt(ls, l, mid);
    bt(rs, mid + 1, r);
    pushup(p);
}
void pushdown(int p, int l, int r) {
    if (tr[p] > 0) tr[ls] = tr[rs] = tr[p];
}
void update(int dl, int dr, int p, int l, int r, int k) {
    if (dl <= l && r <= dr) {
        tr[p] = k;
        return;
    }
    pushdown(p, l, r);
    int mid = (l + r) >> 1;
    if (dl <= mid) update(dl, dr, ls, l, mid, k);
    if (mid < dr) update(dl, dr, rs, mid + 1, r, k);
    pushup(p);
}
void query(int ql, int qr, int p, int l, int r) {
    if (tr[p] > 0) {
        if (!vis[tr[p]]) ans++;
        vis[tr[p]] = 1;
        return;
    }
    if (l == r) return;
    pushdown(p, l, r);
    int mid = (l + r) >> 1;
    if (ql <= mid) query(ql, qr, ls, l, mid);
    if (mid < qr) query(ql, qr, rs, mid + 1, r);
}
int main() {
    SF;
    int T;
    cin >> T;
    while (T--) {
        int n;
        cin >> n;
        int maxr = -1;
        vector<int> v;
        for (int i = 1; i <= n; ++i) {
            cin >> l[i] >> r[i];
            v.push_back(l[i]), v.push_back(r[i]);
        }
        sort(v.begin(), v.end());
        v.erase(unique(v.begin(), v.end()), v.end());
        for (int i = 1; i <= n; ++i) {
            l[i] = lower_bound(v.begin(), v.end(), l[i]) - v.begin() + 1;
            r[i] = lower_bound(v.begin(), v.end(), r[i]) - v.begin() + 1;
        }
        int s = 2 * v.size();
        bt(1, 1, s);
        for (int i = 1; i <= n; ++i) {
            update(l[i], r[i], 1, 1, s, i);
        }
        ans = 0;
        query(1, s, 1, 1, s);
        cout << ans << endl;
        for (int i = 1; i <= s; ++i) vis[i] = 0;
    }
    return 0;
}


这篇关于2022GDUT寒假专题学习-5 树状数组,线段树的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程