\(\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

🕊
posted on 2022-07-09 15:46  Oxide  阅读(66)  评论(2编辑  收藏  举报