Wannafly挑战赛14 前缀查询(Splay)

题目链接

思路:
                   一时卡了忘记字典树了, 写了一大片的 splay s p l a y ,还调试了老久, splay s p l a y 直接加个懒惰标记就行了, 就是和线段树一样,对于前缀为 si s i 的变化和查询,我们可以找出这个前缀所在的位置区间 [l,r] [ l , r ] ,这样将一棵树分成三棵树 [1,l1],[l,r],[r+1,n] [ 1 , l − 1 ] , [ l , r ] , [ r + 1 , n ] ,那么查询和更新的就是中间那棵树,之后再合并下就行了。为了保证一定可以分成三棵树, 可以在初始状态加两个不影响答案的名字。

#include<bits/stdc++.h>
typedef long long ll;
const int maxn = 1e5 + 10;
const int maxnode = 1e5 + 10;
const ll mod = 1e9 + 7;
using namespace std;

struct Splay {
    int root, treapcnt;
    string key[maxnode];
    int pr[maxnode];
    ll val[maxnode], va[maxnode], sum[maxnode];;
    int ch[maxnode][2], cnt[maxnode], sz[maxnode];
    void init() {
        root = 0; treapcnt = 1;
        sz[0] = 0; val[0] = 0;
        sum[0] = 0; va[0] = 0;
        memset(ch[0], 0, sizeof ch[0]);
    }
    void update(int x) {
        sz[x] = sz[ch[x][0]] + sz[ch[x][1]] + cnt[x];
        val[x] = val[ch[x][0]] + val[ch[x][1]] + va[x];
    }

    void push_down(int x) {
        if(!x) return ;
        int ls = ch[x][0], rs = ch[x][1];
        if(ls) { sum[ls] += sum[x]; val[ls] += sum[x] * sz[ls]; }
        if(rs) { sum[rs] += sum[x]; val[rs] += sum[x] * sz[rs]; }
        va[x] += sum[x] * cnt[x]; sum[x] = 0;
        update(x);
    }

    void _rotate(int &x, int t) {
        int y = ch[x][t ^ 1];
        push_down(x); push_down(y);
        ch[x][t ^ 1] = ch[y][t];
        ch[y][t] = x;
        update(x); update(y);
        x = y;
    }
    void __insert(int &x, string new_name, int _val) {
        push_down(x);
        if(x) {
            if(key[x] == new_name) { cnt[x]++; val[x] += _val; va[x] += _val; }
            else {
                int t = key[x] < new_name;
                __insert(ch[x][t], new_name, _val);
                if(pr[ch[x][t]] < pr[x]) _rotate(x, t ^ 1);
            }
        } else {
            x = treapcnt++;
            key[x] = new_name;
            cnt[x] = 1;
            val[x] = _val;
            va[x] = _val;
            sum[x] = 0;
            pr[x] = rand();
            ch[x][0] = ch[x][1] = 0;
        }
        update(x);
    }
    void splay(int &x, int k) {
        push_down(x);
        int cmp, d, siz = sz[ch[x][0]];
        if(k <= siz) cmp = 0;
        else {
            k -= siz + cnt[x];
            if(k <= 0) cmp = -1;
            else cmp = 1;
        }
        if(cmp != -1) {
            int &son = ch[x][cmp];
            int siz2 = sz[ch[son][0]];
            push_down(son);
            if(k <= siz2) d = 0;
            else {
                k -= siz2 + cnt[son];
                if(k <= 0) d = -1;
                else d = 1;
            }
            if(d != -1) {
                splay(ch[son][d], k);
                if(d == cmp) _rotate(x, cmp ^ 1);
                else _rotate(son, cmp);
            }
            _rotate(x, cmp ^ 1);
        }
    }
    int Merge(int &l, int &r) {
        splay(l, sz[l]);
        ch[l][1] = r;
        update(l);
        return l;
    }
    void split(int &o, int k, int &l, int &r) {
        splay(o, k);
        l = o; r = ch[o][1];
        ch[o][1] = 0;
        update(l);
    }
    ll query1(int x, string name) {
        push_down(x);
        if(key[x] == name) return va[x];
        if(!x) return 0;
        if(name < key[x]) return query1(ch[x][0], name);
        else return query1(ch[x][1], name);
    }

    int low_sz(int x, string name) {
        if(!x) return 1;
        push_down(x);
        if(name == key[x]) return sz[ch[x][0]] + 1;
        if(name < key[x]) return low_sz(ch[x][0], name);
        else return sz[ch[x][0]] + cnt[x] + low_sz(ch[x][1], name);
    }

    ll solve(string name, ll flag) {
        int len = name.length() - 1;
        int sz1 = low_sz(root, name);
        name[len]++;
        int sz2 = low_sz(root, name);
        if(sz2 == sz1) return 0;
        int lroot, mid, mi_r, rroot;
        split(root, sz1 - 1, lroot, mid);
        split(mid, sz2 - sz1, mi_r, rroot);
        if(flag != maxn) { sum[mi_r] += flag; val[mi_r] += flag * sz[mi_r]; } //变化fla
        splay(mi_r, 1); ll ans = val[mi_r];
        root = Merge(mi_r, rroot);
        root = Merge(lroot, root);
        return ans;
    }
} sp;


string min_inf, max_inf;
char inf[maxn];

int main() {
    inf[1] = 0; inf[0] = 1; min_inf = inf;
    inf[0] = 255; min_inf = inf;
    int q;
    while(scanf("%d", &q) != EOF) {
        sp.init();
        sp.__insert(sp.root, min_inf, 0);
        sp.__insert(sp.root, max_inf, 0);
        while(q--) {
            int op, val;
            string name;
            scanf("%d", &op); cin >> name;
            if(op == 1) {
                scanf("%d", &val);
                sp.__insert(sp.root, name, val);
            } else if(op == 2) {
                scanf("%d", &val);
                sp.solve(name, val);
            } else if(op == 3) {
                ll ans = sp.query1(sp.root, name);
                printf("%lld\n", ans);
            } else {
                ll ans = sp.solve(name, maxn);
                printf("%lld\n", ans);
            }
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值