P5217 贫穷 题解

提供一个代码短,跑得快的指针 FHQ 写法。

前置知识:FHQ Treap,文艺平衡树笛卡尔树(用来优化常数)。

思路

用平衡树维护之,考虑经典平衡树五问:

节点信息

因为我们写的是 FHQ,所以要记录左右孩子、两个权值、子树大小。

考虑怎么处理 P 操作。我们记录初始文本中每个字符对应的节点,

询问节点 x 的排名时,从根到 x 依次 push down,再从 x 到根统计 x 前面的节点数。

因为要处理 x 到根的路径,所以要记录父节点。

因为要特判 x 不存在的情况,所以要记录该节点是否被删除。

考虑怎么处理 Q 操作。注意到字符集很小,我们记录一个状压值 qq 的第 i 位为 1 表示该子树内包含字符 'a'+i

节点标记

因为要维护区间反转,所以要记录区间反转标记。

下传标记

文艺平衡树一样,不解释。

区间修改

文艺平衡树一样,直接打标记即可,不解释。

上传信息

正常更新子树大小,注意更新父节点。

考虑如何合并状压值 q,显然可以按位或解决。

于是我们可以写出:

struct T
{
    T *l, *r, *f;int v, k, s, q;bool b, g;int z() {return l ? l->s : 0;}
    T(int _) : l(0), r(0), f(0), v(_), k(rand()), s(1), q(1 << v), b(0), g(0) {}void u() {
    s = 1;q = 1 << v;if(l) l->f = this, s += l->s, q |= l->q;if(r) r->f = this, s += r->s, q |= r->q;}
    void d() {if(b) {T *t = l;l = r;r = t;if(l) l->b ^= 1;if(r) r->b ^= 1;b = 0;}}
}*r, *a, *b, *c, *s[30], *d[100050];void D(T *x) {if(x) D(x->f), x->d();}
void S(T *x, int k, T *&a, T *&b)
{
    if(!x) {a = b = 0;return;}if(!k) {a = 0;b = x;return;}
    if(k >= x->s) {a = x;b = 0;return;}x->d();int z = x->z();
    if(z >= k) b = x, S(x->l, k, a, b->l), b->u();
    else a = x, S(x->r, k - z - 1, a->r, b), a->u();
}
T *M(T *a, T *b)
{
    if(!a) return b;if(!b) return a;
    if(a->k < b->k) {a->d();a->r = M(a->r, b);a->u();return a;}
    else {b->d();b->l = M(a, b->l);b->u();return b;}
}

考虑怎么把初始文本建出来。我们可以用笛卡尔树优化建树过程:

for(int i = 1;i <= n;++i)
{
    do v = getchar();while(!islower(v));a = new T(v - 'a');
    k = a->k;p = l;while(l && k < s[l - 1]->k) s[--l]->u();
    if(l < p) a->l = s[l];if(l) s[l - 1]->r = a;s[l++] = d[i] = a;
}
while(l) s[--l]->u();r = s[0];

接下来分别考虑每种操作。

I 操作/D 操作

套路分裂合并,不解释。

scanf(" %c%d", &o, &x);switch(o) {
case 'I': scanf(" %c", &v);S(r, x, a, b);r = M(a, M(new T(v - 'a'), b));break;
case 'D': S(r, x - 1, a, b);S(b, 1, b, c);b->g = 1;r = M(a, c);break;

R 操作

文艺平衡树一样,把操作区间分裂出来,打标记,合并回去。

case 'R': scanf("%d", &y);S(r, y, a, c);S(a, x - 1, a, b);b->b ^= 1;r = M(a, M(b, c));break;

P 操作

见上文。

询问节点 x 的排名时,从根到 x 依次 push down,再从 x 到根统计 x 前面的节点数。

case 'P': q = 0;if(!d[x]->g) {D(a = d[x]);q = a->z() + 1;
for(;a->f;a = a->f) if(a == a->f->r) q += a->f->z() + 1;}printf("%d\n", q);break;

T 操作

套路分裂合并,不解释。

case 'T': S(r, x - 1, a, b);S(b, 1, b, c);printf("%c\n", b->v + 'a');r = M(a, M(b, c));break;

Q 操作

把操作区间分裂出来,输出 q 的二进制中 1 的个数(可以用 __builtin_popcount 计算),合并回去。

case 'Q':scanf("%d", &y);S(r, y, a, c);S(a, x - 1, a, b);
printf("%d\n", __builtin_popcount(b->q));r = M(a, M(b, c));break;

Code

来点极简风代码

#include <cstdio>
#include <cctype>
#include <cstdlib>
#define A r = M(a, M(b, c))
#define B S(r, x - 1, a, b);S(b, 1, b, c)
#define C S(r, R(), a, c);S(a, x - 1, a, b)
inline int R()
{
    int r = 0;char c = getchar();while(!isdigit(c)) c = getchar();
    while(isdigit(c)) r = r * 10 + c - '0', c = getchar();return r;
}
int n, m, l, k, p, o, x, y, q;char v;struct T
{
    T *l, *r, *f;int v, k, s, q;bool b, g;int z() {return l ? l->s : 0;}
    T(int _) : l(0), r(0), f(0), v(_), k(rand()), s(1), q(1 << v), b(0), g(0) {}void u() {
    s = 1;q = 1 << v;if(l) l->f = this, s += l->s, q |= l->q;if(r) r->f = this, s += r->s, q |= r->q;}
    void d() {if(b) {T *t = l;l = r;r = t;if(l) l->b ^= 1;if(r) r->b ^= 1;b = 0;}}
}*r, *a, *b, *c, *s[30], *d[100050];void D(T *x) {if(x) D(x->f), x->d();}
void S(T *x, int k, T *&a, T *&b)
{
    if(!x) {a = b = 0;return;}if(!k) {a = 0;b = x;return;}
    if(k >= x->s) {a = x;b = 0;return;}x->d();int z = x->z();
    if(z >= k) b = x, S(x->l, k, a, b->l), b->u();
    else a = x, S(x->r, k - z - 1, a->r, b), a->u();
}
T *M(T *a, T *b)
{
    if(!a) return b;if(!b) return a;
    if(a->k < b->k) {a->d();a->r = M(a->r, b);a->u();return a;}
    else {b->d();b->l = M(a, b->l);b->u();return b;}
}
int main()
{
    srand(388651);n = R();m = R();for(int i = 1;i <= n;++i)
    {
        do v = getchar();while(!islower(v));a = new T(v - 'a');
        k = a->k;p = l;while(l && k < s[l - 1]->k) s[--l]->u();
        if(l < p) a->l = s[l];if(l) s[l - 1]->r = a;s[l++] = d[i] = a;
    }
    while(l) s[--l]->u();r = s[0];while(m--)
    {
        scanf(" %c", &o);x = R();switch(o)
        {
            case 'I': scanf(" %c", &v);S(r, x, a, b);r = M(a, M(new T(v - 'a'), b));break;
            case 'D': B;b->g = 1;r = M(a, c);break;case 'R': C;b->b ^= 1;A;break;
            case 'P': q = 0;if(!d[x]->g) {D(a = d[x]);q = a->z() + 1;for(;a->f;a = a->f)
            if(a == a->f->r) q += a->f->z() + 1;}printf("%d\n", q);break;
            case 'T': B;printf("%c\n", b->v + 'a');A;break;
            case 'Q': C;printf("%d\n", __builtin_popcount(b->q));A;break;
        }
    }
    return 0;
}
posted @   Jijidawang  阅读(6)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示