首页 > 其他分享 >P2710 数列/P2042 [NOI2005] 维护数列

P2710 数列/P2042 [NOI2005] 维护数列

时间:2024-09-18 21:02:10浏览次数:1  
标签:数列 idx int sum tr NOI2005 P2710 key tag

题意(以 P2710 为例)

image

思路

使用 FHQ-Treap 进行求解,清晰明了。

  1. 对于 insert,先将要插入的数建成一棵树,然后将这棵树放入 FHQ-Treap 中。
  2. 对于 delete,将要删除的树分离出来,然后把剩下的部分合并即可,将删除的树的树根丢到废弃节点的栈中以备以后使用(节约空间,不然 MLE)。
  3. 对于 reverse,给一个节点打上标签并立即交换,参考文艺平衡树代码。注意,如果存在 MAKE-SAME 操作的标签,那么不用反转,因为所有数字都是一样的。
  4. 对于 make-same,将这个节点的 reverse 的标记清空(用不到,理由如上),给这个节点打上标记并立即更新结构体中所有数字。
  5. 对于 get-sum,pushup 维护即可。
  6. 对于 get,与普通平衡树的看排名为 \(x\) 的数字是什么类似。
  7. 对于 max-sum,最大子段和,参考 GSS-1,注意这是平衡树,对于根节点的处理细节非常多。

代码

细节非常多,注意对废弃节点的处理。(代码以 P2710 为准)

#include <bits/stdc++.h>

using namespace std;
using i64 = long long;

const int N = 500010;
const i64 inf = 1e18;

struct node {
    int l, r, size, rnd;
    int tag_same, tag_rev;
    i64 key, lsum, rsum, sum, ans;
} tr[N];

int rt, cnt;
int del_ver[N], top;
int n, m;
int a[N], sz_a;

int newnode(int key) {
    int idx;
    if (!top) idx = ++cnt;
    else {
        idx = del_ver[top];
        top--;
        if (tr[idx].l) del_ver[++top] = (tr[idx].l);
        if (tr[idx].r) del_ver[++top] = (tr[idx].r);
    }
    tr[idx].l = tr[idx].r = 0;
    tr[idx].rnd = rand();
    tr[idx].size = 1;
    tr[idx].tag_same = -1e9;
    tr[idx].tag_rev = 0;
    tr[idx].key = tr[idx].lsum = tr[idx].rsum = tr[idx].sum = tr[idx].ans = key;
    return idx;
}

void addtagrev(int u) {
    if (!u) return;
    if (tr[u].tag_same != -1e9) return;
    tr[u].tag_rev ^= 1;
    swap(tr[u].l, tr[u].r);
    swap(tr[u].lsum, tr[u].rsum);
}

void addtagsame(int u, int key) {
    if (!u) return;
    if (tr[u].tag_rev) tr[u].tag_rev = 0;
    tr[u].tag_same = tr[u].key = key;
    tr[u].sum = key * tr[u].size;
    tr[u].lsum = tr[u].rsum = tr[u].ans = max(tr[u].key, tr[u].sum);
}

void pushdown(int u) {
    if (tr[u].tag_same != -1e9) {
        tr[u].tag_rev = 0;
        addtagsame(tr[u].l, tr[u].tag_same);
        addtagsame(tr[u].r, tr[u].tag_same);
        tr[u].tag_same = -1e9;
    }
    if (tr[u].tag_rev) {
        addtagrev(tr[u].l);
        addtagrev(tr[u].r);
        tr[u].tag_rev = 0;
    }
}

void pushup(int u) {
    tr[u].size = tr[tr[u].l].size + tr[tr[u].r].size + 1;
    tr[u].lsum = max({tr[tr[u].l].lsum, tr[tr[u].l].sum + tr[u].key, tr[tr[u].l].sum + tr[tr[u].r].lsum + tr[u].key});
    tr[u].rsum = max({tr[tr[u].r].rsum, tr[tr[u].r].sum + tr[u].key, tr[tr[u].r].sum + tr[tr[u].l].rsum + tr[u].key});
    tr[u].sum = tr[tr[u].l].sum + tr[tr[u].r].sum + tr[u].key;
    tr[u].ans = max({tr[tr[u].l].ans, tr[tr[u].r].ans, tr[u].key, tr[tr[u].l].rsum + tr[u].key, tr[tr[u].r].lsum + tr[u].key, tr[tr[u].l].rsum + tr[u].key + tr[tr[u].r].lsum});
}

void split(int u, int sz, int &x, int &y) {
    if (!u) {
        x = y = 0;
        return;
    }
    pushdown(u);
    if (tr[tr[u].l].size + 1 <= sz) {
        x = u;
        split(tr[u].r, sz - tr[tr[u].l].size - 1, tr[u].r, y);
    }
    else {
        y = u;
        split(tr[u].l, sz, x, tr[u].l);
    }
    pushup(u);
}

int merge(int x, int y) {
    if ((!x) || (!y)) return x | y;
    if (tr[x].rnd < tr[y].rnd) {
        pushdown(x);
        tr[x].r = merge(tr[x].r, y);
        pushup(x);
        return x;
    }
    else {
        pushdown(y);
        tr[y].l = merge(x, tr[y].l);
        pushup(y);
        return y;
    }
}

void del(int l, int r) {
    int x, y, z;
    split(rt, r, x, z);
    split(x, l - 1, x, y);
    del_ver[++top] = (y);
    rt = merge(x, z);
}

int build(int l, int r) {
    if (l == r) return newnode(a[l]);
    int mid = (l + r) >> 1;
    int x = build(l, mid);
    int y = build(mid + 1, r);
    return merge(x, y);
}

void insert(int p) {
    int x, y;
    int rt2 = build(1, sz_a);
    split(rt, p, x, y);
    rt = merge(merge(x, rt2), y);
}

void make_same(int l, int r, int c) {
    int x, y, z;
    split(rt, r, x, z);
    split(x, l - 1, x, y);
    addtagsame(y, c);
    rt = merge(merge(x, y), z);
}

void reverse_arr(int l, int r) {
    int x, y, z;
    split(rt, r, x, z);
    split(x, l - 1, x, y);
    addtagrev(y);
    rt = merge(merge(x, y), z);
}

i64 get_sum(int l, int r) {
    int x, y, z;
    split(rt, r, x, z);
    split(x, l - 1, x, y);
    i64 ans = tr[y].sum;
    rt = merge(merge(x, y), z);
    return ans;
}

i64 get_max(int l, int r) {
    int x, y, z;
    split(rt, r, x, z);
    split(x, l - 1, x, y);
    i64 ans = tr[y].ans;
    rt = merge(merge(x, y), z);
    return ans;
}

int get_num(int u, int rk) {
    pushdown(u);
    if (tr[tr[u].l].size + 1 == rk) return tr[u].key;
    else if (tr[tr[u].l].size >= rk) return get_num(tr[u].l, rk);
    else return get_num(tr[u].r, rk - tr[tr[u].l].size - 1);
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    cin >> n >> m;

    tr[0].lsum = tr[0].rsum = tr[0].ans = -inf;
    for (int i = 1; i <= n; i++) cin >> a[i];
    sz_a = n;
    insert(0);

    string opt;
    int x, y, z;
    for (int T = 1; T <= m; T++) {
        cin >> opt;
        if (opt == "INSERT") {
            cin >> x >> sz_a;
            for (int i = 1; i <= sz_a; i++) cin >> a[i];
            insert(x);
        }
        else if (opt == "DELETE") {
            cin >> x >> y;
            del(x, x + y - 1);
        }
        else if (opt == "MAKE-SAME") {
            cin >> x >> y >> z;
            make_same(x, x + y - 1, z);
        }
        else if (opt == "REVERSE") {
            cin >> x >> y;
            reverse_arr(x, x + y - 1);
        }
        else if (opt == "GET-SUM") {
            cin >> x >> y;
            cout << get_sum(x, x + y - 1) << '\n';
        }
        else if (opt == "GET") {
            cin >> x;
            cout << get_num(rt, x) << '\n';
        }
        else {
            cin >> x >> y;
            cout << get_max(x, x + y - 1) << '\n';
        }
    }
    return 0;
}

标签:数列,idx,int,sum,tr,NOI2005,P2710,key,tag
From: https://www.cnblogs.com/Yuan-Jiawei/p/18419323

相关文章

  • 打卡信奥刷题(769)用Scratch图形化工具信P5722[普及组/提高组] 【深基4.例11】数列求和
    【深基4.例11】数列求和题目描述计算1+2+3+⋯......
  • P2294 [HNOI2005] 狡猾的商人 两种做法
    贪心#include<bits/stdc++.h>#defineintlonglongusingnamespacestd;constintN=1e3+100;intn,m;structNODE{ intl,r,val; booloperator<(constNODE&h)const { if(l!=h.l) returnl>h.l; returnr>h.r; }};priority_queue......
  • P2201 数列编辑器(对顶栈)
    include<bits/stdc++.h>usingnamespacestd;definexfirstdefineysecondtypedefpair<int,int>PII;typedeflonglongll;typedefunsignedlonglongull;typedefunsignedintuint;typedefvectorVS;typedefvectorVI;typedefvector<vect......
  • [ZZULIOJ] 1041: 数列求和2 (两种方法)
    1.题目描述输入一个整数n,输出数列1-1/3+1/5-……前n项的和。输入:输入只有一个整数n。输出:结果保留2为小数,单独占一行。样例输入Copy3样例输出Copy0.872.方法一#include<iomanip>#include<iostream>usingnamespacestd;doublek=1,i,sum=0;intn;intma......
  • 高等数学 1.2数列的极限
    目录数列极限的定义数列的概念数列极限的定义收敛数列的性质数列极限的定义数列的概念如果按照某一法则,对每个\(n\in\mathbb{N}_+\),对应着一个确定的实数\(x_n\),这些实数\(x_n\)按照下标\(n\)从大到小排列得到的一个序列\[x_1,x_2,x_3,\cdots,x_n,\cdots,\]就......
  • 数列分块入门
    分块是一种优秀的思想。“数据”是分块的目的。不同于大多数树形数据结构,分块中访问数据是容易的,因此,它可以用比前者更简单的方式支持复杂的操作。“标记”是分块最重要的过程。不同于大多数树形数据结构,分块大多数时候不需要支持标记与标记合并,因此,它能完成一些前者不能完成的......
  • PAT乙级 1030 完美数列 测试点3.4
    一、题目二、代码#include<iostream>#include<vector>#include<algorithm>usingnamespacestd;boolcmp(longlonga,longlongb){ returna<b;}intmain(){ longlongn,p; cin>>n>>p; longlongnum=0,temp=0; ve......
  • 超越常规:斐波那契数列的极速计算技术3
    针对斐波那契数列算法进行详细介绍和优化,从最简单的递归方法到更高效的迭代、缓存、动态规划、公式推导和矩阵解法,最终达到了时间复杂度为O(logn)的快速幂矩阵解法来感受算法的神奇之处,最后可以看到如何实现在输入n=2100000000(21亿)时仅耗时0.02毫秒的最佳效果。一、回顾斐波......
  • 例2.12 分别编写求n!和输出斐波那契数列的函数,并用两个函数进行测试
    例2.12分别编写求n!和输出斐波那契数列的函数,并用两个函数进行测试2.12.1deffactorial(n):r=1whilen>1:r*=nn-=1returnrdeffib(n):a,b=1,1whilea<n:print(a,end="")a,b=b,a+bprint('%d!=%d'%(......
  • 洛谷 B3645 数列前缀和 2 题解
    前缀知识:枚举,费马小定理,逆元,线性乘法逆元,线段树(?)。解法1:暴力如题。暴力枚举即可,30分。由于太简单,不给代码。解法2:前缀积+费马小定理+逆元由于涉及静态区间,可以想到前缀积。前缀积公式为\(q_r/q_{l-1}\),除法恰好可以用逆元来算。直接写即可。不会超时,因为时间为\(O(n\logp)\)......