[CCPC2022 广州站]B. Ayano and sequences
Description
给定长为 \(n\) 的序列 \(a\);长为 \(n\) 的 \(b,c\) 序列初始全为 \(0\)。\(q\) 次操作:
- 给出 \(l,r,d\),把 \(a_l,a_{l+1},\dots,a_r\) 全部赋为 \(d\)。
- 给出 \(l,r,k\),把 \(c_l,c_{l+1},\dots,c_r\) 全部加上 \(k\)。
每次操作后,\(b_{a_i}\) 会加上 \(c_i\)。给出所有操作后的 \(b\) 序列,对 \(2^{64}\) 取模。
\(1 \le n,q \le 5 \cdot 10^5\),\(1 \le d, a_i \le n\),\(1 \le k,c_i \le 10^9\)。
Solution
原序列 \(a\) 中不会有超过 \(n\) 个值连续段。一次 1. 操作在 \(a\) 中顶多把一个连续段劈成 \(3\) 个部分。因此,共不超过 \(q\) 次 1. 操作,一共均摊下来 \(\mathcal O(n)\) 个连续段。就是 ODT,因为只要求你维护连续段(不需要暴力遍历),复杂度是对的,为 \(\mathcal O(n\log n)\)。
使用 ODT 维护颜色段,一个 \([l_i,r_i]\) 的连续段,记录连续相同值 \(v_i\),他的出现时间 \(s_i\),删除时间 \(t_i\)。
把操作离线下来,所有连续段记录下来后,按时间维护 \(c\) 的变化,然后对于每个 \(b_i\) 统计答案。
令 \(h_i\) 等于每次修改后 \(c_i\) 之和。有两种维护方案。如果对于线段树掌握熟练,可以跳过下面一段。
方案一
树状数组维护 \(h\) 的差分数组,令 \(f_i = c_{i}-c_{i-1}\),一次区间加操作为 \(f_l\gets f_l+k\),\(f_{r+1}\gets f_{r+1}-k\),把它整体往 \(b\) 里加,则 \(f_i \gets f_i+\displaystyle \sum _{j \le i}{f_j}\);
一个 \(f_j\) 可能经历多次加减法,令第 \(t\) 次操作后 \(f_j\) 增加了 \(k_{j,t}\)。把第 \(T\) 次修改后 \(f_i\) 的式子展开:
其中的 \((T-t+1)k_{j,t}\) 显然可以拆成 \((T+1)\times k_{j,t}-t\times k_{j,t}\)。
建立两棵线段树或树状数组,任务就是对于每个 \(j\) 维护第二个 \(\sum\) 之后的那一坨值,两棵树分别维护两坨值来搞定,那两坨值可以用我们所熟知的区间加法维护。至于查询,应该是区间查询。
方案二
线段树暴力维护即可,把 \(c_i\) 看作斜率,然后维护直线就 over 了?
我写的第一种。
接下来,借助 \(h\) 的维护方法,我们思考普遍性的解法。\(j\in[l_i,r_i]\) 这一段的 \(c_j\) 都会赋给 \(a_{v_i}\)。其实就相当于 \(a_{v_i}\) 加上在 \(t_i\) 时间时的 \(\displaystyle \sum_{j=l}^r h_j\),再减去在 \(s_i-1\) 时间时的 \(\displaystyle \sum_{j=l}^r h_j\)。可以理解为前缀和与差分,这样做了过后就是 \(i\in[l,r]:h_i\) 的变化量之和。
于是在这些地方打上标记,按时间顺序遍历,动态改 \(h\) 序列,同时修改 \(a_{v_i}\) 即可。
code
gym 上的题看不到提交记录,就直接放代码了。
#include <bits/stdc++.h>
#define FASTIO ios::sync_with_stdio(0), cin.tie(0), cout.tie(0)
#define rep(i, l, r) for (int i = l; i <= r; i++)
#define per(i, r, l) for (int i = r; i >= l; i--)
using namespace std;
const int N = 5e5+5, M = 4e5 + 5, mod = 998244353;
int n, m, a[N];
struct node {
int l, r, t;
mutable int v;
node(int l,int r,int t,int v=0) : l(l),r(r),t(t),v(v) {}
inline bool operator<(const node& b) const { return l<b.l; }
};
struct Dtat { int l, r, lt, rt, c; };
vector<Dtat> ops, ydt;
set<node> odt;
inline auto split(int x, int t) {
auto it = odt.lower_bound(node(x,0,0));
if (it != odt.end() && it->l == x) return it; --it;
int l = it->l, r = it->r, v = it->v;
ydt.emplace_back(l,r,it->t,t-1,it->v);
odt.erase(it), odt.insert(node(l, x-1, t, v));
return odt.insert(node(x, r, t, v)).first;
}
inline void assign(int l, int r, int v,int t){
auto rt = split(r+1,t), lt = split(l,t);
for (auto it = lt; it != rt; ++it)
ydt.emplace_back(it->l,it->r,it->t,t-1,it->v);
odt.erase(lt,rt);
odt.insert(node(l,r,t,v));
}
typedef unsigned long long ull;
struct SegmentTree {
struct node {
ull dat, tag;
} tr[N<<2];
#define dat(p) tr[p].dat
#define tag(p) tr[p].tag
#define lc(p) (p<<1)
#define rc(p) (p<<1|1)
#define mid (l+r>>1)
inline void pushdown(int p, int l, int r) {
if (tag(p)) {
dat(lc(p)) += (mid-l+1)*tag(p), tag(lc(p)) += tag(p);
dat(rc(p)) += (r-mid)*tag(p), tag(rc(p)) += tag(p);
tag(p) = 0;
}
}
inline void add(int p, int l, int r, int L, int R, ull v) {
if (L <= l && r <= R) return dat(p) += v*(r-l+1), tag(p) += v, void();
pushdown(p, l, r);
if (L <= mid) add(lc(p), l, mid, L, R, v);
if (mid < R) add(rc(p), mid+1, r, L, R, v);
dat(p) = dat(lc(p)) + dat(rc(p));
}
inline ull ask(int p, int l, int r, int L, int R) {
if (L <= l && r <= R) return dat(p);
pushdown(p, l, r);
if (L > mid) return ask(rc(p), mid+1, r, L, R);
if (mid >= R) return ask(lc(p), l, mid, L, R);
return ask(lc(p), l, mid, L, R)+ask(rc(p), mid+1, r, L, R);
}
} t1, t2;
ull res[N];
int main() {
FASTIO;
cin >> n >> m;
rep(i, 1, n) cin >> a[i], odt.insert(node(i,i,1,a[i]));
odt.insert(node(n+1,n+1,0));
rep(i, 1, m) {
int op, l, r, w;
cin >> op >> l >> r >> w;
if (op == 1) assign(l,r,w,i);
else ops.emplace_back(l,r,i,w,0);
}
assign(1,n,0,m+1);
for (auto [l,r,lt,rt,c] : ydt)
if (lt <= rt) {
ops.emplace_back(l,r,lt-1,c,-1);
ops.emplace_back(l,r,rt,c,1);
}
stable_sort(ops.begin(), ops.end(), [](const Dtat& a, const Dtat& b) {
return a.lt == b.lt ? abs(a.c) < abs(b.c) : a.lt < b.lt;
});
auto it = ops.begin();
rep(i, 0, m) {
for (; it!=ops.end() && it->lt <= i; ++it) {
auto [l,r,t,v,op] = *it;
if (op == 0) t1.add(1,1,n,l,r,(ull)t*v), t2.add(1,1,n,l,r,v);
else res[v] += (t2.ask(1,1,n,l,r)*(t+1)-t1.ask(1,1,n,l,r))*op;
}
}
rep(i, 1, n) cout << res[i] << ' ';
return 0;
}

浙公网安备 33010602011771号