• [Contest on 2022.7.8] 好久没写平衡树了


    \(\cal T_1\) 数列维护 100 合 1 / sequence

    Description

    你有一个长度为 \(n\) 的数列,现在你要支持以下 \(100\) 种操作:

    • 操作 \(001\):询问若可以使用膜法一次令任一区间内的所有数同时 \(+1\) 或同时 \(−1\),要把某一区间恰好都变为 \(0\),至少需要几次膜法;
    • 操作 \(010\):使一个区间内的所有数加上 \(x\)
    • 操作 \(011\):翻转一个区间;
    • 操作 \(100\):你突然需要大量的膜力,需要回到 \(k\) 次操作之前的状态。此操作也同样视为一种操作。无论之前是否进行过操作 \(100\),假设此时是第 \(i\) 次操作,此处就会回到第 \(i − k\) 次操作之前的状态。

    \(n,m\leqslant 10^5,|a_i|,|x|\leqslant 10000,0\leqslant k_i<i\)空间限制 \(\text{32 MiB}\).

    Solution

    给大家带来一些欢乐:这题考场上我只会 \(\cal O(nm)\) 的部分分,而且每次查询还是用笛卡尔树做的 ,更下饭的是我把这个部分分写挂了。

    假设查询 \([l,r]\) 的答案,这个答案事实上就是前后补充两个零,将序列增长至 \([l-1,r+1]\)\([l,r+1]\) 之间的 差分值之和。你可以这样理解:每个点都可以无偿补给后面连续不增且比它矮的点。

    考虑用平衡树维护正差分值之和。操作 \(2\) 是容易维护的,这里只探讨操作 \(3\):这事实上就是将 \((l,r]\) 中的差分值翻转再乘上 \(-1\),然后特别计算位置 \(l,r+1\)。难道为了特别计算需要维护原序列吗?事实上并不用,考虑位置 \(l\) 上的差分值,实际上就是 \(a_l-a_{l-1}\rightarrow a_r-a_{l-1}\),其 \(\delta=a_r-a_l\),而这就是 \((l,r]\) 区间的差分值之和,位置 \(r+1\) 上的差分值同理。于是再维护负差分值之和即可。

    操作 \(4\) 直接搞一个操作树即可,因为所有操作都是可撤销的。

    Code

    实现并不难,但是太久没写平衡树导致出了很多非常神必的 bug:比方说将下标 \(1\) 写成了下标 \(2\)(神奇的是数组只开了 \(0,1\),它却没有错而且甚至过了很多组拍),比方说把 \(-1\) 写成了 \(1\).

    最神奇的是自己瞪眼竟然瞪出来了。

    # include <cstdio>
    # include <cctype>
    # define print(x,y) write(x), putchar(y)
    
    template <class T>
    inline T read(const T sample) {
        T x=0; char s; bool f=0;
        while(!isdigit(s=getchar())) f|=(s=='-');
        for(; isdigit(s); s=getchar()) x=(x<<1)+(x<<3)+(s^48);
        return f? -x: x;
    }
    template <class T> 
    inline void write(T x) {
        static int writ[50], w_tp=0;
        if(x<0) putchar('-'), x=-x;
        do writ[++w_tp]=x-x/10*10, x/=10; while(x);
        while(putchar(writ[w_tp--]^48), w_tp);
    }
    
    # include <vector>
    # include <iostream>
    using namespace std;
    typedef long long _long;
    
    const int maxn = 1e5+5;
    
    vector <int> vec[maxn]; _long ans[maxn];
    int n, m, stk[maxn], tp, cnt, rt;
    struct node {
        bool tag;
        int key, siz, ls, rs; 
        _long val, s[2];
    } t[maxn];
    struct ask { int opt,l,r,k; } q[maxn];
    
    int gen() {
        static int SEED = 20051002;
        static const int Mod = (1ll<<31)-1;
        return SEED = 1ll*SEED*1145%Mod;
    }
    _long Abs(const _long& V) { return V>0?V:0; }
    int getV(int val,bool opt) { 
        if(!opt) return val>=0? val: 0;
        return val<0? val: 0;
    }
    int newnode(int Val) {
        t[++cnt].key=gen(), t[cnt].val=Val;
        t[cnt].s[0]=getV(Val,0), 
        t[cnt].s[1]=getV(Val,1),
        t[cnt].ls=t[cnt].rs=0;
        t[cnt].siz=1; return cnt;
    }
    void pushUp(int o) {
        if(!o) return; t[o].siz = t[t[o].ls].siz+t[t[o].rs].siz+1,
        t[o].s[0] = t[t[o].ls].s[0]+t[t[o].rs].s[0]+getV(t[o].val,0),
        t[o].s[1] = t[t[o].ls].s[1]+t[t[o].rs].s[1]+getV(t[o].val,1);
    }
    void rev(int o) {
        if(!o) return; swap(t[o].ls,t[o].rs);
        t[o].tag ^= 1, swap(t[o].s[0],t[o].s[1]);
        t[o].val *= -1, t[o].s[0] *= -1, t[o].s[1] *= -1;
    } // mistake '1' for '-1' ;))))
    void pushDown(int o) {
        if(!o || !t[o].tag) return;
        rev(t[o].ls), rev(t[o].rs), t[o].tag=0;
    }
    int merge(int x,int y) {
        if(!x || !y) return x|y;
        pushDown(x), pushDown(y);
        if(t[x].key<t[y].key) {
            t[x].rs = merge(t[x].rs,y),
            pushUp(x); return x;
        } else {
            t[y].ls = merge(x,t[y].ls),
            pushUp(y); return y;
        }
    }
    void split(int o,int k,int& x,int& y) {
        if(!o) return x=y=0, void();
        pushDown(o);
        if(t[t[o].ls].siz+1<=k) x=o, split(
            t[o].rs,k-t[t[o].ls].siz-1,t[o].rs,y);
        else y=o, split(t[o].ls,k,x,t[o].ls); pushUp(o);
    }
    void update(int o) {
        if(!o) return; update(t[o].ls),
        update(t[o].rs), pushUp(o);
    }
    _long query(int l,int r) {
        static int x,y; static _long ans;
        split(rt,r,rt,y), ans = Abs(-(t[rt].s[0]+t[rt].s[1])),
        split(rt,l,rt,x), ans = ans+t[x].s[0]+Abs(t[rt].s[0]+t[rt].s[1]);
        rt = merge(rt,merge(x,y)); return ans;
    }
    void modify(int l,int r,int k) {
        static int x,y,z,u;
        split(rt,l,rt,x), split(rt,l-1,rt,y);
        t[y].val+=k, t[y].s[0]=getV(t[y].val,0),
        t[y].s[1]=getV(t[y].val,1), 
        split(x,r-l+1,x,z), split(x,r-l,x,u);
        t[u].val-=k, t[u].s[0]=getV(t[u].val,0),
        t[u].s[1]=getV(t[u].val,1),
        rt = merge(rt,merge(merge(y,x),merge(u,z)));
    }
    void reverse(int l,int r) {
        static int x,y,z,u;
        split(rt,l,rt,x), split(x,r-l,x,y);
        split(rt,l-1,rt,z), split(y,1,y,u);
        const _long delta = t[x].s[0]+t[x].s[1];
        t[z].val+=delta, t[z].s[0]=getV(t[z].val,0),
        t[z].s[1]=getV(t[z].val,1), t[y].val+=delta, rev(x),
        t[y].s[0]=getV(t[y].val,0), t[y].s[1]=getV(t[y].val,1);
        rt = merge(rt,merge(merge(z,x),merge(y,u)));
    }
    
    void ins(int Val) {
        int x = newnode(Val), tmp=0;
        while(t[stk[tp-1]].key>t[x].key) 
            tmp=stk[--tp]; t[x].ls=tmp;
        t[stk[tp-1]].rs=x, stk[tp++]=x;
    }
    void consTree() {
        t[0].key=-1, tp=1; int now;
        for(int i=1, pre=0; i<=n; ++i) 
            ins((now=read(9))-pre), pre=now;
        ins(-now); rt=t[0].rs; 
        t[0].rs=0, update(rt);
    }
    
    void beelzebul(int now) {
        if(q[now].opt==1) ans[now]=query(q[now].l,q[now].r);
        if(q[now].opt==2) modify(q[now].l,q[now].r,q[now].k);
        if(q[now].opt==3) reverse(q[now].l,q[now].r);
        for(const auto& v:vec[now]) beelzebul(v);
        if(q[now].opt==2) modify(q[now].l,q[now].r,-q[now].k);
        if(q[now].opt==3) reverse(q[now].l,q[now].r);
    }
    
    int main() {
    	freopen("sequence.in","r",stdin);
        freopen("sequence.out","w",stdout);
        n=read(9), m=read(9); consTree();
        char opt[6]; int l,r,k,x,y,z,u;
        for(int i=1;i<=m;++i) {
            scanf("%s",opt); ans[i]=-1;
            if(opt[0]=='0' && opt[1]=='0' && opt[2]=='1') {
                q[i].l=read(9), q[i].r=read(9), q[i].opt=1;
                vec[i-1].emplace_back(i);
            } else if(opt[0]=='0' && opt[2]=='0') {
                q[i].l=read(9), q[i].r=read(9), q[i].k=read(9), q[i].opt=2;
                vec[i-1].emplace_back(i);
            } else if(opt[0]=='0') {
                q[i].l=read(9), q[i].r=read(9), q[i].opt=3;
                vec[i-1].emplace_back(i);
            } else vec[i-read(9)-1].emplace_back(i);
        } beelzebul(0);
        for(int i=1;i<=m;++i)
            if(~ans[i]) print(ans[i],'\n');
    	return 0;
    }
    

    \(\cal T_2\) 整数拆分 / partition

    Description

    定义 \(f_m(n)\) 表示将 \(n\) 表示为若干 \(m\) 的非负整数次幂的和的方案数。定义 \(g_m^k(n)\)\(k\)\(f_m(n)\) 卷起来的结果。

    给定 \(n, m, k\),请求出 \(\displaystyle \left(\sum_{i=0}^n g_m^k(i)\right) \bmod \left(10^9 + 7\right)\).

    \(0\leqslant n\leqslant 10^{18},2\leqslant m\leqslant 10^{18},1\leqslant k\leqslant 20\).

    Solution

    朴素的思路是先用背包把 \(f_m\) 算出来,这个复杂度大概是 \(\mathcal O(n\log_m n)\) 级别的。然后再任意模数卷积就可以得到 \(n\leqslant 10^5\) 的部分分。不过实际上并不需要这么麻烦,可以尝试理解卷积的意义 —— 比如 \(f_m\)\(f_m\) 卷在一起就是把 \(n\) 划分成两个部分,每个部分被表示成若干 \(m\) 的非负整数次幂的和的方案数。于是可以直接在背包外面套 \(k\) 个循环直接算,复杂度为 \(\mathcal O(kn\log_m n)\).

    Code

    
    
  • 相关阅读:
    链表习题(2)-一个集合用带头结点的单链表L表示,编写算法删除其值最大的结点。
    ubuntu14安装
    poi多sheet练习
    vmware虚拟机网络模式-仅主机模式
    vmware虚拟机网络模式-NAT模式
    vmware虚拟机网络模式-桥接模式
    IntelliJ IDEA 创建maven
    IntelliJ IDEA
    冒泡
    Java泛型 通配符? extends与super
  • 原文地址:https://www.cnblogs.com/AWhiteWall/p/16459911.html
Copyright © 2020-2023  润新知