首页 > 其他分享 >P2042 [NOI2005] 维护数列 题解

P2042 [NOI2005] 维护数列 题解

时间:2024-02-17 16:11:22浏览次数:34  
标签:const int 题解 pos return NOI2005 P2042 lTree curr

题目链接:维护数列

比较不好码的题,首先确保自己会一种文艺平衡树的书写,这点因人而异,比较推荐初学者学 \(fhq\) 平衡树,坑比较少,比较好码,写平衡树合并、持久化类的题时,也比较好写。注意到空间需求比较大,还涉及删除,我们的删除用各种树类数据结构中最常用的回收标记用于新的节点开辟。

对于添加一个段,这个简单,我们为添加的元素使用笛卡尔树建树,然后分裂插入点,由于这三棵平衡树的序我们是知道的,所以可以直接用 \(fhq\) 的 \(merge\) 依次合并三棵树,不需要使用平衡树合并。这个的复杂度显然是 \(O(tot)\) 建树,\(O(log{n})\) 实现。

对于删除一个段很简单,直接合并这个段两侧的平衡树即可,树高为 \(\log{n}\) 所以复杂度为 \(\log{n}\),并在删除栈中加入这棵树的根节点,当如果要复用根节点的标记时,如果有左右子树,再加入左右子树的根进入删除栈中留作新节点空间使用。

关于翻转和覆盖很简单,常规打标记,一开始还以为还有增加标记。

关于最大子段和,这个问题在线段树树当中是一个经典的问题,不过本题实测空段 \(0\) 是不能作为答案的,所以我们可以为 \(0\) 处赋个无穷小,防止空子树造成影响。但常规的最长前后缀并不影响,最长前后缀显然可以为空,所以可以与 \(0\) 取最大。然后区间上最大子段要么为左子树的要么为右子树的,或者前后缀拼到当前单点的段。

所有操作复杂度为:\(O(\log{n})\),除了建树需要 \(O(tot)\)。

参照代码
#include <bits/stdc++.h>

// #pragma GCC optimize(2)
// #pragma GCC optimize("Ofast,no-stack-protector,unroll-loops,fast-math")
// #pragma GCC target("sse,sse2,sse3,ssse3,sse4.1,sse4.2,avx,avx2,popcnt,tune=native")

#define isPbdsFile

#ifdef isPbdsFile

#include <bits/extc++.h>

#else

#include <ext/pb_ds/priority_queue.hpp>
#include <ext/pb_ds/hash_policy.hpp>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/trie_policy.hpp>
#include <ext/pb_ds/tag_and_trait.hpp>
#include <ext/pb_ds/hash_policy.hpp>
#include <ext/pb_ds/list_update_policy.hpp>
#include <ext/pb_ds/assoc_container.hpp>
#include <ext/pb_ds/exception.hpp>
#include <ext/rope>

#endif

using namespace std;
using namespace __gnu_cxx;
using namespace __gnu_pbds;
typedef long long ll;
typedef long double ld;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef tuple<int, int, int> tii;
typedef tuple<ll, ll, ll> tll;
typedef unsigned int ui;
typedef unsigned long long ull;
typedef __int128 i128;
#define hash1 unordered_map
#define hash2 gp_hash_table
#define hash3 cc_hash_table
#define stdHeap std::priority_queue
#define pbdsHeap __gnu_pbds::priority_queue
#define sortArr(a, n) sort(a+1,a+n+1)
#define all(v) v.begin(),v.end()
#define yes cout<<"YES"
#define no cout<<"NO"
#define Spider ios_base::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
#define MyFile freopen("..\\input.txt", "r", stdin),freopen("..\\output.txt", "w", stdout);
#define forn(i, a, b) for(int i = a; i <= b; i++)
#define forv(i, a, b) for(int i=a;i>=b;i--)
#define ls(x) (x<<1)
#define rs(x) (x<<1|1)
#define endl '\n'
//用于Miller-Rabin
[[maybe_unused]] static int Prime_Number[13] = {0, 2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37};

template <typename T>
int disc(T* a, int n)
{
    return unique(a + 1, a + n + 1) - (a + 1);
}

template <typename T>
T lowBit(T x)
{
    return x & -x;
}

template <typename T>
T Rand(T l, T r)
{
    static mt19937 Rand(time(nullptr));
    uniform_int_distribution<T> dis(l, r);
    return dis(Rand);
}

template <typename T1, typename T2>
T1 modt(T1 a, T2 b)
{
    return (a % b + b) % b;
}

template <typename T1, typename T2, typename T3>
T1 qPow(T1 a, T2 b, T3 c)
{
    a %= c;
    T1 ans = 1;
    for (; b; b >>= 1, (a *= a) %= c)if (b & 1)(ans *= a) %= c;
    return modt(ans, c);
}

template <typename T>
void read(T& x)
{
    x = 0;
    T sign = 1;
    char ch = getchar();
    while (!isdigit(ch))
    {
        if (ch == '-')sign = -1;
        ch = getchar();
    }
    while (isdigit(ch))
    {
        x = (x << 3) + (x << 1) + (ch ^ 48);
        ch = getchar();
    }
    x *= sign;
}

template <typename T, typename... U>
void read(T& x, U&... y)
{
    read(x);
    read(y...);
}

template <typename T>
void write(T x)
{
    if (typeid(x) == typeid(char))return;
    if (x < 0)x = -x, putchar('-');
    if (x > 9)write(x / 10);
    putchar(x % 10 ^ 48);
}

template <typename C, typename T, typename... U>
void write(C c, T x, U... y)
{
    write(x), putchar(c);
    write(c, y...);
}


template <typename T11, typename T22, typename T33>
struct T3
{
    T11 one;
    T22 tow;
    T33 three;

    bool operator<(const T3 other) const
    {
        if (one == other.one)
        {
            if (tow == other.tow)return three < other.three;
            return tow < other.tow;
        }
        return one < other.one;
    }

    T3() { one = tow = three = 0; }

    T3(T11 one, T22 tow, T33 three) : one(one), tow(tow), three(three)
    {
    }
};

template <typename T1, typename T2>
void uMax(T1& x, T2 y)
{
    if (x < y)x = y;
}

template <typename T1, typename T2>
void uMin(T1& x, T2 y)
{
    if (x > y)x = y;
}

constexpr int N = 4e6 + 10;
constexpr ll INF = 1e18 + 7;
int del[N << 1], delCnt;

struct FHQ
{
    int val, siz, rnk, rev;
    ll sum, cov;
    ll preMx, nxtMx, mx;
    int child[2];
} node[N];

#define left(x) node[x].child[0]
#define right(x) node[x].child[1]
#define siz(x) node[x].siz
#define rnk(x) node[x].rnk
#define val(x) node[x].val
#define sum(x) node[x].sum
#define cov(x) node[x].cov
#define preMX(x) node[x].preMx
#define nxtMX(x) node[x].nxtMx
#define mx(x) node[x].mx
#define rev(x) node[x].rev

inline void push_up(const int curr)
{
    mx(0) = -INF;
    siz(curr) = siz(left(curr)) + siz(right(curr)) + 1;
    sum(curr) = sum(left(curr)) + sum(right(curr)) + val(curr);
    preMX(curr) = max({preMX(left(curr)), sum(left(curr)) + val(curr) + preMX(right(curr)), 0LL});
    nxtMX(curr) = max({nxtMX(right(curr)), sum(right(curr)) + val(curr) + nxtMX(left(curr)), 0LL});
    mx(curr) = val(curr) + nxtMX(left(curr)) + preMX(right(curr));
    uMax(mx(curr), max(mx(left(curr)),mx(right(curr))));
}


inline void Cover(const int curr, const ll val)
{
    cov(curr) = val(curr) = val;
    sum(curr) = val * siz(curr);
    preMX(curr) = nxtMX(curr) = max(sum(curr), 0LL);
    mx(curr) = max(val,sum(curr));
}

inline void Rev(const int curr)
{
    swap(left(curr),right(curr));
    swap(preMX(curr),nxtMX(curr));
    rev(curr) ^= 1;
}

inline void push_down(const int curr)
{
    if (cov(curr) != INF)
    {
        Cover(left(curr),cov(curr));
        Cover(right(curr),cov(curr));
        cov(curr) = INF;
    }
    if (rev(curr))
    {
        Rev(left(curr)), Rev(right(curr));
        rev(curr) = 0;
    }
}

int cnt;

inline int newNode(const int val)
{
    const int curr = delCnt ? del[delCnt--] : ++cnt;
    if (left(curr))del[++delCnt] = left(curr);
    if (right(curr))del[++delCnt] = right(curr);
    siz(curr) = 1;
    preMX(curr) = nxtMX(curr) = max(val, 0);
    val(curr) = sum(curr) = mx(curr) = val;
    cov(curr) = INF, rev(curr) = left(curr) = right(curr) = 0;
    rnk(curr) = Rand(1, static_cast<int>(1e9));
    return curr;
}

inline void split(const int curr, const int k, int& x, int& y)
{
    if (!curr)
    {
        x = y = 0;
        return;
    }
    push_down(curr);
    if (siz(left(curr)) < k)
    {
        x = curr;
        split(right(curr), k - siz(left(curr)) - 1,right(x), y);
        push_up(x);
        return;
    }
    y = curr;
    split(left(curr), k, x,left(y));
    push_up(y);
}

inline int merge(const int x, const int y)
{
    if (!x or !y)return x ^ y;
    if (rnk(x) < rnk(y))
    {
        push_down(x);
        right(x) = merge(right(x), y);
        push_up(x);
        return x;
    }
    push_down(y);
    left(y) = merge(x,left(y));
    push_up(y);
    return y;
}

inline int merge(const int l, const int mid, const int r)
{
    return merge(merge(l, mid), r);
}

int a[N], root;

inline int build(const int n)
{
    int ans = 0;
    stack<int> st;
    forn(i, 1, n)
    {
        int last = 0;
        const int curr = newNode(a[i]);
        while (!st.empty() and rnk(st.top()) > rnk(curr))push_up(last = st.top()), st.pop();
        if (!st.empty())right(st.top()) = curr;
        left(curr) = last, st.push(curr);
    }
    while (!st.empty())
    {
        if (st.size() == 1)ans = st.top();
        push_up(st.top()), st.pop();
    }
    return ans;
}

inline void Insert(const int pos, const int idx)
{
    int lTree, rTree;
    split(root, pos, lTree, rTree); //[0,pos]、[pos+1,...]
    const int newTree = build(idx);
    root = merge(lTree, newTree, rTree);
}

inline void Delete(const int l, const int r)
{
    int lTree, rTree;
    split(root, r, lTree, rTree);
    int lson, rson;
    split(lTree, l - 1, lson, rson);
    del[++delCnt] = rson;
    root = merge(lson, rTree);
}

inline void Assign(const int l, const int r, const int val)
{
    int lTree, rTree;
    split(root, r, lTree, rTree);
    int lson, rson;
    split(lTree, l - 1, lson, rson);
    Cover(rson, val);
    root = merge(lson, rson, rTree);
}

inline void Reverse(const int l, const int r)
{
    int lTree, rTree;
    split(root, r, lTree, rTree);
    int lson, rson;
    split(lTree, l - 1, lson, rson);
    Rev(rson);
    root = merge(lson, rson, rTree);
}

inline ll QuerySum(const int l, const int r)
{
    int lTree, rTree;
    split(root, r, lTree, rTree);
    int lson, rson;
    split(lTree, l - 1, lson, rson);
    ll ans = sum(rson);
    root = merge(lson, rson, rTree);
    return ans;
}

int n, m;
string s;

inline void solve()
{
    cin >> n >> m;
    forn(i, 1, n)cin >> a[i];
    root = build(n);
    while (m--)
    {
        cin >> s;
        int pos, tot;
        if (s == "INSERT")
        {
            cin >> pos >> tot;
            forn(i, 1, tot)cin >> a[i];
            Insert(pos, tot);
        }
        else if (s == "DELETE")
        {
            cin >> pos >> tot;
            Delete(pos, pos + tot - 1);
        }
        else if (s == "MAKE-SAME")
        {
            int val;
            cin >> pos >> tot >> val;
            Assign(pos, pos + tot - 1, val);
        }
        else if (s == "REVERSE")
        {
            cin >> pos >> tot;
            Reverse(pos, pos + tot - 1);
        }
        else if (s == "GET-SUM")
        {
            cin >> pos >> tot;
            cout << QuerySum(pos, pos + tot - 1) << endl;
        }
        else cout << mx(root) << endl;
    }
}

signed int main()
{
    // MyFile
    Spider
    //------------------------------------------------------
    // clock_t start = clock();
    int test = 1;
    //    read(test);
    // cin >> test;
    forn(i, 1, test)solve();
    //    while (cin >> n, n)solve();
    //    while (cin >> test)solve();
    // clock_t end = clock();
    // cerr << "time = " << double(end - start) / CLOCKS_PER_SEC << "s" << endl;
}

标签:const,int,题解,pos,return,NOI2005,P2042,lTree,curr
From: https://www.cnblogs.com/Athanasy/p/18018073

相关文章

  • CF1929E 题解
    题意:给定一棵\(n\)个节点数和\(k\)条路径\((a_i,b_i)\),求至少将多少条边染色,使得给定路径都至少有一条染色的边。\(n\le10^5,k\le20\)。思路:好题。显然状压\(dp\),\(dp[S]\)表示至少染多少条边使得\(S\)中的路径都满足条件。正常思路是枚举子集转移,考虑\(T\s......
  • 整数划分 题解
    题目描述如何把一个正整数N(N长度<20)划分为M(M>1)个部分,使这M个部分的乘积最大。N、M从键盘输入,输出最大值及一种划分方式。输入格式第一行一个正整数T(T<=10000),表示有T组数据。接下来T行每行两个正整数N,M。输出格式对于每组数据第一行输出最大值。第二行输出划分方案,将N按......
  • 情书密码 题解
    题目描述有消息称:绝恋找到了自己的MissRight,正准备自己的表白。绝恋已经写好了情书,但为了避免其它人截获,他对情书进行加密。为了打探绝恋的私密,你冒着生命危险终于搞到了这封情书。原以为可以轻易将情书解密,结果竟然发现聪明的绝恋并没有直接写出加密用的密码,而是在那粉红色的......
  • CF484B题解
    朴素的办法是枚举每两个数然后更新取模的结果。时间复杂度为\(O(n^2)\)不能通过。这个朴素的过程可以看作枚举一个数然后找对其取模最大值的过程。我们可以枚举一个数,然后再枚举以它的倍数为两端的区间,找其中取模的最大值。找最大值的过程可以二分或双指针。值域很小,也可以......
  • 牛客小白87题解A-G
    A小苯的石子游戏本题贪心模拟即可B小苯的排序疑惑考虑到最简单的操作把n-1个数排好,最后一个看能否有序。即:a[1]<=min(a[2],a[3],a[4]..,a[n])||a[n]>=max(a[1],a[2],a[3]....,a[n-1])满足条件,反之易得不可能C&D小苯的IDE括号问题本题考察题意理解,简单版本我们可以只关注逻......
  • 0217-0224校赛部分题解
    SMUWinter2024Round#3(Div.2)对于自己比较有价值的题目是D题https://codeforces.com/gym/102897/problem/D?mobile=trueJ题https://codeforces.com/gym/102897/problem/JK题https://codeforces.com/gym/102897/problem/KE题https://codeforces.com/gym/102897/prob......
  • 关于thrift python接口和java通信出现问题解决
    真的无语,搞了一个下午。使用thrift出现错误,先说一下遇到第一个错误,如下图:那时候代码是这叼样```if__name__=='__main__':handler=MessageServiceHandler()processor=MessageService.Processor(handler)transport=TSocket.TServerSocket(None,"9090"......
  • P3157 [CQOI2011] 动态逆序对 题解
    题目链接:动态逆序对常见经典题,理解一个数对逆序对贡献计算即可。对于一个数而言,它在一个序列中的逆序对有两部分贡献,一部分为前面比它严格大的数,另一部分为后面比它严格小的数,有道二莫题也是基于此去考虑的。考虑最开始知道了总逆序数,每次删除一个数逆序数会减少两部分值,显然就......
  • 选课 题解
    题目描述大学里实行学分。每门课程都有一定的学分,学生只要选修了这门课并考核通过就能获得相应的学分。学生最后的学分是他选修的各门课的学分的总和。每个学生都要选择规定数量的课程。其中有些课程可以直接选修,有些课程需要一定的基础知识,必须在选了其它的一些课程的基础上才......
  • 洛谷 P4065 题解
    模拟赛T1,纪念一下第一次场切紫。(话说场上这么多人切是不是都找到原了,就我这么傻想了半天)正难则反,很容易的将题目转化为选择若干种颜色,使这些颜色在原数组中的位置连续。设$pre_i$为颜色$i$最早出现的位置,$suf_i$为颜色$i$最晚出现的位置。假设通过选择若干颜色得到的位......