首页 > 其他分享 >题解:P5217 贫穷

题解:P5217 贫穷

时间:2024-08-25 21:07:31浏览次数:10  
标签:P5217 node lc 题解 rc fa 贫穷 str siz

题意

维护一个字符串,支持以下操作:

  • \(\texttt{I x c}\),在第 \(x\) 个字母后面插入一个 \(c\)。
  • \(\texttt{D x}\),删除第 \(x\) 个字母。
  • \(\texttt{R x y}\),反转当前文本中的区间 \([x,y]\)。
  • \(\texttt{P x}\),输出初始文本中第 \(x\) 个字母在当前文本中的位置。特别地,若不存在,输出 \(0\)。
  • \(\texttt{T x}\),输出当前文本中第 \(x\) 个字母。
  • \(\texttt{Q x y}\),输出当前文本中区间 \([x,y]\) 内出现过的字母的种类数。

分析

序列问题,有插入操作,直接无脑上平衡树。

本题解使用 FHQ Treap。


我们先看这一个操作:

  • \(\texttt{P x}\),输出初始文本中第 \(x\) 个字母在当前文本中的位置。特别地,若不存在,输出 \(0\)。

发现一般的 FHQ Treap 无法维护相关信息。

考虑记录每个节点的父亲节点

从该节点向根跳,如果该节点是父亲节点的右儿子,那么记录父亲节点及其左子树贡献。

新的问题来了:如何维护父亲节点?

我们可以考虑在 push_up() 操作时维护,将两个子节点的父节点设为当前节点。

对于个别节点(split()merge() 操作后的根节点),在操作完后单独将父节点设为空即可。

该部分代码如下:

void access(node *x) 
{
    if(!x) return; 
    access(x->fa); 
    x->push_down();
}

uint32_t find_pos(node *x)
{
    if(x->del||!x) return 0;
    access(x);
    uint32_t res=siz(x->lc)+1;
    while(x->fa)
    {
        if(x->fa->rc==x) 
            res+=siz(x->fa->lc)+1;
        x=x->fa;
    }
    return res;
}

然后来看查询操作:

  • \(\texttt{Q x y}\),输出当前文本中区间 \([x,y]\) 内出现过的字母的种类数。

可以维护一个 bitset,记录哪些字母是出现过的。

push_up() 代码如下:

node* push_up()
{
    siz=1;
    chr.reset();
    chr[c-'a']=1;
    if(lc) siz+=lc->siz, chr|=lc->chr, lc->fa=this;
    if(rc) siz+=rc->siz, chr|=rc->chr, rc->fa=this;
    return this;
}

查询本身就很套路了:

int query(int l, int r)
{
    node *a, *b, *c;
    split(str, l-1, a, b);
    split(b, r-l+1, b, c);
    int ret=b->chr.count();
    str=merge(merge(a, b), c);
    str->fa=0;
    return ret;
}

其他的都是 FHQ Treap 的基本操作,不多赘述。

Code

#include<bits/stdc++.h>
using namespace std;
#define maxn 200005

namespace Treap
{
    mt19937 rnd(time(0));

    struct node
    {
        uint64_t id;
        int64_t siz=1;
        node *lc=0, *rc=0, *fa=0;
        char c;
        bool del=0, rev=0;
        bitset<26> chr;
        node(char ch, uint64_t d): c(ch), id(d) {chr.reset(); chr[ch-'a']=1;}

        void reverse() {swap(lc, rc); rev^=1;}

        void push_down()
        {
            if(rev) 
            {
                if(lc) lc->reverse();
                if(rc) rc->reverse();
                rev^=1;
            }
        }

        node* push_up()
        {
            siz=1;
            chr.reset();
            chr[c-'a']=1;
            if(lc) siz+=lc->siz, chr|=lc->chr, lc->fa=this;
            if(rc) siz+=rc->siz, chr|=rc->chr, rc->fa=this;
            return this;
        }
    };

    node* new_node(char v) {return new node(v, rnd());}

    #define siz(x) (x?x->siz:0)

    void split(node *x, int s, node *&l, node *&r)
    {
        if(!x) return l=r=0, void();
        x->push_down();
        if(siz(x->lc)<s) l=x, split(x->rc, s-siz(x->lc)-1, x->rc, r);
        else             r=x, split(x->lc, s, l, x->lc);
        x->push_up();
    }

    node* merge(node *x, node *y)
    {
        if(!x||!y) return x?x:y;
        if(x->id<y->id)
        {
            x->push_down();
            x->rc=merge(x->rc, y);
            return x->push_up();
        }
        else
        {
            y->push_down();
            y->lc=merge(x, y->lc);
            return y->push_up();
        }
    }

    void access(node *x) 
    {
        if(!x) return; 
        access(x->fa); 
        x->push_down();
    }
    
    uint32_t find_pos(node *x)
    {
        if(x->del||!x) return 0;
        access(x);
        uint32_t res=siz(x->lc)+1;
        while(x->fa)
        {
            if(x->fa->rc==x) 
                res+=siz(x->fa->lc)+1;
            x=x->fa;
        }
        return res;
    }

    node *str;

    void insert(int p, char c)
    {
        node *a, *b;
        split(str, p, a, b);
        str=merge(merge(a, new_node(c)), b);
        str->fa=0;
    }

    void erase(int p)
    {
        node *a, *b, *c;
        split(str, p-1, a, b);
        split(b, 1, b, c);
        b->del=1;
        str=merge(a, c);
        if(str) str->fa=0;
    }

    void reverse(int l, int r)
    {
        node *a, *b, *c;
        split(str, l-1, a, b);
        split(b, r-l+1, b, c);
        b->reverse();
        str=merge(merge(a, b), c);
        str->fa=0;
    }

    char get(int p)
    {
        node *a, *b, *c;
        split(str, p-1, a, b);
        split(b, 1, b, c);
        char ret=b->c;
        str=merge(merge(a, b), c);
        str->fa=0;
        return ret;
    }

    int query(int l, int r)
    {
        node *a, *b, *c;
        split(str, l-1, a, b);
        split(b, r-l+1, b, c);
        int ret=b->chr.count();
        str=merge(merge(a, b), c);
        str->fa=0;
        return ret;
    }
}

Treap::node *rt[maxn];
string s;

int main()
{
    int n, m;
    cin>>n>>m>>s;
    for(int i=1;i<=n;i++) 
        rt[i]=Treap::new_node(s[i-1]), 
        Treap::str=merge(Treap::str, rt[i]);
    while(m--)
    {
        char op;
        int x, y;
        char ch;
        cin>>op>>x;
        if(op=='I') cin>>ch, Treap::insert(x, ch);
        if(op=='D') Treap::erase(x);
        if(op=='R') cin>>y, Treap::reverse(x, y);
        if(op=='P') cout<<Treap::find_pos(rt[x])<<'\n';
        if(op=='T') cout<<Treap::get(x)<<'\n';
        if(op=='Q') cin>>y, cout<<Treap::query(x, y)<<'\n';
    }
}

标签:P5217,node,lc,题解,rc,fa,贫穷,str,siz
From: https://www.cnblogs.com/redacted-area/p/18379546

相关文章

  • 题解:UVA11996 Jewel Magic
    题意给你一个01串,要求完成以下操作:单点插入。单点删除。区间翻转。查询两点开始的LCP。分析先看查询操作,如何得到LCP的长度?我们可以考虑二分长度\(l\),然后用哈希检验区间\([p1,p1+l-1]\)是否等于区间\([p2,p2+l-1]\)。平衡树维护哈希即可。发现......
  • 题解:AT_abc354_g [ABC354G] Select Strings
    题目分析题意给定\(n\)个字符串,要求从中选出若干个组成一个集合,且集合中每个字符串都互不包含。求集合中字符串的权值的和的最大值。分析首先很容易想到用KMP判两个串是否存在包含关系。考虑建图,将不能同时存在于一个集合中的串的节点相连。然后发现只需求出这个图的最......
  • 题解:P7952 [✗✓OI R1] 天动万象
    提供一种和第一篇题解不同的理解思路。题目分析看到操作\(1\):拿dfs序水水就行了。看到操作\(2\):???特殊情况我们考虑一下特殊情况下操作\(2\)怎么处理。假如这棵树是一条链。设从根到叶节点权值如下:(随便赋的)节点编号123456权值123456如果我们......
  • 题解:CF1995C Squaring
    题意给定序列\(a\),每次操作可以使\(a_i\getsa_i^2\),求使\(a\)不降的最少操作次数。分析因为\(1^k=1\),所以无解的情况只有\(\exist\a_i=1\)且\(\exist\j\in[1,i),a_j>1\)。在有解的情况下,假设对\(a_{i-1}\)操作\({k_{i-1}}\)次,对\(a_i\)操作\({k_i}\)次。......
  • 题解:UVA1479 Graph and Queries
    分析先看删边操作。由于并不保证是森林,所以我们没有好的方法来在线维护删边相关的操作。所以,我们可以套路地把询问离线,然后倒着操作。删边变成加边。需要注意的是权值的修改,记录时要把当前权值和修改的权值反过来。然后我们发现这个操作很经典,维护方式和[HNOI2012]永无乡......
  • 题解:P7475 「C.E.L.U-02」简易输入法
    题意给定词典\(\text{U}\),每次询问读入一个字符串\(s\),以及一个整数\(x\)对于这个字符串有以下几种情形:设\(s_i\in\text{U}\)且\(s\)为\(s_i\)的前缀的个数为\(a\)。当\(a\gex\)时,请输出按照以输出次数从大到小为第一关键字,以字典序为第二关键字排序后的第\(x......
  • 题解:P7401 [COCI2020-2021#5] Planine
    题意现有一座上下起伏的山。它可以抽象为一个包含\(n\)(\(n\)为奇数)个点\((x_i,y_i)\)以及\((x_1,-\inf)\)与\((x_n,-\inf)\)的多边形。对于所有满足\(i\neq1\),\(i\neqn\),\(i\bmod2=1\)的整数\(i\),\((x_i,y_i)\)都是山谷。现要放置若干个高度为\(h\)的点光......
  • 题解:SP1182 SORTBIT - Sorted bit squence
    题意将区间\([m,n]\)的所有整数按照其二进制位表示的数中\(1\)的数量从小到大排序。如果\(1\)的数量相同,则按照数的大小排序。求序列中第\(k\)个数。其中,负数使用补码来表示:一个负数的二进制数与其相反数的二进制数之和恰好等于\(2^{32}\)。分析考虑用uint32_t存......
  • 题解:P3266 [JLOI2015] 骗我呢
    题意有一个\(n\timesm\)的数组\(x_{i,j}(1\lei\len,1\lej\lem)\),满足:\(x_{i,j}\in[0,m]\)\(\foralli\in[1,n],\forallj\in[1,m),x_{i,j}<x_{i,j+1}\)\(\foralli\in(1,n],\forallj\in[1,m),x_{i,j}<x_{i-1,j+1}\)......
  • 题解:CF70D Professor's task
    题意实现以下两种操作:往点集\(S\)中添加一个点\((x,y)\)。询问点\((x,y)\)是否在点集\(S\)的凸包中。分析动态凸包板子。建议先完成P2521[HAOI2011]防线修建。上题维护的是上半个凸包,本题维护上下两个。将凸包中的点按\(x\)排序,通过\((x,y)\)前驱......