loading...

[CCPC2022 广州站]B. Ayano and sequences

链接

Description

给定长为 \(n\) 的序列 \(a\);长为 \(n\)\(b,c\) 序列初始全为 \(0\)\(q\) 次操作:

  1. 给出 \(l,r,d\),把 \(a_l,a_{l+1},\dots,a_r\) 全部赋为 \(d\)
  2. 给出 \(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\) 的式子展开:

\[\begin{aligned} f_i&=\sum_{j \le i} \left\{k_{j,1}+(k_{j,1}+k_{j,2})+\dots+\sum_ {t=1}^T k_{j,t}\right\}\\ &=\sum_{j \le i}\sum_{t=1}^T (T-t+1)k_{j,t}\\ \end{aligned} \]

其中的 \((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;
}
posted @ 2025-02-20 17:14  goldspade  阅读(40)  评论(0)    收藏  举报