1 条题解

  • 0
    @ 2025-8-24 23:07:30

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar ケロシ
    Blue Archive

    搬运于2025-08-24 23:07:30,当前版本为作者最后更新于2024-12-26 16:15:30,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    还是我,我又来介绍复杂度与 LL 无关的算法了(喜提最劣解)。

    首先是朴素的 dp,设 fi,jf_{i,j} 为在字符串前 ii 位中,选了 jj 个哞叫子串的最小代价,那么转移很明显:

    $$w(l,r)=[s_l=\mathrm{O} ]a_l+\sum_{i=l+1}^{r}[s_i=\mathrm{M} ]a_i $$fi,j=min(fi1,j,fiL,j1+w(iL+1,i))f_{i,j}=\min(f_{i-1,j},f_{i-L,j-1}+w(i-L+1,i))

    然后不难发现每个 fif_i 都是凸的,所以考虑使用 Slope Trick 维护这个。

    套路的,设 fif_i 的差分数组 gig_i,那么 gi,j=fi,jfi,j1g_{i,j}=f_{i,j}-f_{i,j-1}。然后用 gig_i 反代 fif_i,即 fi,j=k=1jgi,kf_{i,j}=\sum_{k=1}^{j}g_{i,k},然后代入 dp 式子,然后就能写出关于 gi,jg_{i,j} 的转移式:

    $$\begin{aligned} g_{i,j}&=f_{i,j}-f_{i,j-1}\\ &=\min(f_{i-1,j},f_{i-L,j-1}+w(i-L+1,i))\\ &-\min(f_{i-1,j_1},f_{i-L,j-2}+w(i-L+1,i))\\ &=\min(\sum_{k=1}^{j}g_{i-1,k},\sum_{k=1}^{j-1}g_{i-L,k}+w(i-L+1,i))\\ &-\min(\sum_{k=1}^{j-1}g_{i-1,k},\sum_{k=1}^{j-2}g_{i-L,k}+w(i-L+1,i))\\ \end{aligned} $$

    左右两个 min\min 差不多,所以直接观察 k=1jgi1,k\sum_{k=1}^{j}g_{i-1,k}k=1j1giL,k+w(iL+1,i))\sum_{k=1}^{j-1}g_{i-L,k}+w(i-L+1,i)) 的关系。

    我们发现一个东西,就是对于上面的式子,存在一个分割点 pp,满足 jpj \le pmin\min 取到前者,j>pj>p 的取到后者。

    这个比较难证明,大概的理解就是左边的式子 fi1f_{i-1} 相较右边 fiLf_{i-L} 右移了一位,导致了这个 fi1f_{i-1} 的变化更大,也就是更凸,到了分割点后就一直比 fiLf_{i-L} 小了。

    知道了存在分割点 pp 后,不难发现上面关于 gi,jg_{i,j} 的转移可以按照 pp 进行分类:

    $$g_{i,j}= \left\{\begin{matrix} g_{i-1,j} & j \le p\\ \sum_{k=1}^{j-1} g_{i-L,k}+w(i-L+1,i)-\sum_{k=1}^{j-1} g_{i-1,k} & j=p+1\\ g_{i-L,j-1} & j > p + 1 \end{matrix}\right. $$

    接下来讲讲实现。

    对于这个 gi,jg_{i,j} 的转移,一段是从 gi1g_{i-1} 拉过来,还有一段从 giLg_{i-L} 拉过来,所以需要持久化平衡树维护。

    对于找分割点 pp,需要先二分这个 pp,然后去平衡树上查询一遍,这一部分是瓶颈,是 O(log2n)O(\log^2 n) 的。

    本人使用了 Leafy Tree 进行实现,还需注意这题的空间很小,需要定期把无用的点删掉。

    时间复杂度 O(nlog2n)O(n \log^2 n),与 LL 无关。

    平衡树常数很大,跑很慢。

    const int N = 3e5 + 5;
    const int M = 1e7 + 5;
    const ll LNF = 1e18;
    int n, k, m; 
    string t;
    ll a[N], s[N];
    int rub[M], tp, lim;
    int rt[N], tot, ls[M], rs[M], sz[M]; ll F[M];
    bool vis[M];
    int add() {
    	lim ++;
    	if(tp) return rub[tp --];
    	return ++ tot;
    }
    int add(ll x) {
    	int u = add();
    	ls[u] = rs[u] = 0;
    	sz[u] = 1;
    	F[u] = x;
    	return u;
    }
    void up(int u) {
    	sz[u] = sz[ls[u]] + sz[rs[u]];
    	F[u] = F[ls[u]] + F[rs[u]];
    }
    int up(int l, int r) {
    	int u = add();
    	ls[u] = l, rs[u] = r;
    	up(u);
    	return u;
    }
    int merge(int u, int v) {
    	if(! u || ! v) return u | v;
    	if(sz[u] <= sz[v] * 4 && sz[v] <= sz[u] * 4) {
    		return up(u, v);
    	}
    	if(sz[u] >= sz[v]) {
    		int l = ls[u], r = rs[u];
    		if(sz[l] * 4 > (sz[u] + sz[v])) return merge(l, merge(r, v));
    		return merge(merge(l, ls[r]), merge(rs[r], v));
    	}
    	else {
    		int l = ls[v], r = rs[v];
    		if(sz[r] * 4 > (sz[u] + sz[v])) return merge(merge(u, l), r);
    		return merge(merge(u, ls[l]), merge(rs[l], r));
    	}
    }
    void split(int u, int p, int & x, int & y) {
    	if(! u || ! p) {
    		x = 0, y = u;
    		return;
    	}
    	if(sz[u] == p) {
    		x = u, y = 0;
    		return;
    	}
    	if(p <= sz[ls[u]]) {
    		split(ls[u], p, x, y);
    		y = merge(y, rs[u]);
    	}
    	else {
    		split(rs[u], p - sz[ls[u]], x, y);
    		x = merge(ls[u], x);
    	}
    }
    ll query(int u, int r) {
    	if(! u || ! r) return 0;
    	if(sz[u] <= r) {
    		return F[u];
    	}
    	if(r <= sz[ls[u]]) return query(ls[u], r);
    	return F[ls[u]] + query(rs[u], r - sz[ls[u]]);
    }
    ll ans[N]; int cnt;
    void print(int u) {
    	if(! ls[u]) {
    		ans[++ cnt] = F[u];
    		return;
    	}
    	print(ls[u]);
    	print(rs[u]);
    }
    void dfs(int u) {
    	vis[u] = 1;
    	if(! ls[u]) {
    		return;
    	}
    	dfs(ls[u]);
    	dfs(rs[u]);
    }
    void recycle(int l, int r) {
    	FOR(i, 1, tot) vis[i] = 0;
    	FOR(i, l, r) dfs(rt[i]);
    	FOR(i, 1, tot) if(sz[i] && ! vis[i]) {
    		sz[i] = 0; 
    		rub[++ tp] = i;
    		lim --;
    	}
    }
    void solve() {
    	cin >> k >> n; m = n / k;
    	cin >> t; t = ' ' + t;
    	FOR(i, 1, n) cin >> a[i];
    	FOR(i, 1, n) s[i] = s[i - 1] + (t[i] == 'O' ? 0 : a[i]);
    	REP(i, k) rt[i] = add(LNF);
    	FOR(i, k, n) {
    		if(lim > M - 200) recycle(i - k, i - 1);
    		int L = 1, R = i / k, pos = 0;
    		ll cost = s[i] - s[i - k + 1] + (t[i - k + 1] == 'M' ? 0 : a[i - k + 1]);
    		while(L <= R) {
    			int mid = L + R >> 1;
    			ll vl = query(rt[i - 1], mid);
    			ll vr = query(rt[i - k], mid - 1) + cost;
    			if(vl <= vr) {
    				pos = mid;
    				L = mid + 1;
    			}
    			else {
    				R = mid - 1;
    			}
    		}
    		ll vl = query(rt[i - 1], pos + 1);
    		ll vr = query(rt[i - k], pos) + cost;
    		if(pos == i / k) {
    			rt[i] = rt[i - 1];
    		}
    		else if(! pos && vl > vr) {
    			rt[i] = merge(add(cost), rt[i - k]);
    		}
    		else {
    			int x, y, z;
    			split(rt[i - 1], pos, x, z);
    			split(rt[i - k], pos, z, y);
    			ll val = query(rt[i - k], pos) - query(rt[i - 1], pos) + cost;
    			rt[i] = merge(merge(x, add(val)), y);
    		}
    	}
    	print(rt[n]);
    	FOR(i, 1, n / k) ans[i] += ans[i - 1];
    	FOR(i, 1, n / k) cout << ans[i] << endl;
    }
    
    • 1

    信息

    ID
    11172
    时间
    3000ms
    内存
    256MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者