CF1682F 题解

2022/6/9 23:25:46

本文主要是介绍CF1682F 题解,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

CF1682F MCMF?

高难度紫属于是

反正题解写起来还挺复杂度,代码就还行

第一步

拆点。把 \(i\) 拆成 \(|b_i|\) 个点,然后这 \(|b_i|\) 个点和之前一样连边。

此时就是左右有相同数量的点,然后两个点 \((u, v)\) 相连的花费是 \(|a_u-a_v|\),要把左右都匹配的最小花费。

此时最优解显然是左右分别按 \(a_i\) 排序,然后左边 \(i\) 连右边 \(i\)。

可以反证,设有一个别的构造,连了 \((u_i,v_i),(u_j,v_j),u_i\neq v_i, u_j\neq v_j\),读者自证不难。

第二步

此时已经有 \(\Theta(\sum b_i)\) 回答询问的做法了,显然寄。瓶颈在于绝对值,考虑拆绝对值。

一个 \((a_i,b_i), (b_i>0)\),即一个右半部分的点的贡献是正还是负取决于前面还剩下多少个左半部分的点;记前缀和 \(s_i=\sum\limits_{k=1}^i b_i\)。

(为方便,下文中 \(s\) 出现时,实际意义上下标均含 \(-1\),于是将 \(s\) 右移,下文的 \(s_p\) 表示实际的 \(s_p-1\))

对于一个询问 \([l,r]\),可以枚举每个 \(b_i\),在 \(O(r-l)\) 内做完:

  1. \(b_i > 0, \Delta =s_{i} - s_{l} < 0\),左边部分的点还有剩下的。
    1. \(|b_i| \le |\Delta|\):贡献是 \(a_i|b_i|\);
    2. \(|b_i| > |\Delta|\):贡献是 \(2a_i|\Delta|-a_i|b_i|\)。
  2. \(b_i>0, \Delta \ge 0\),没有左半部分点,贡献是 \(-a_i|b_i|\)。

\(b_i<0\) 同理。

第三步

现在复杂度是 \(O(nq)\) 的,显然寄。考虑能否快速回答询问。

记后缀 \(i\) 的答案为 \(\mathrm{ans}_i\),会发现询问 \([l,r]\) 的答案为 \(\mathrm{ans}_l - \mathrm{ans}_{r+1}\)。(前缀也就算了,为什么会有人观察后缀性质啊喂

原因在于,\(\sum\limits_{i=l}^rb_i=0\),对于后缀 \(l\) 的答案,\([l,r]\) 和 \((r, n]\) 是不会相互影响的,计算 \((r, n]\) 时的起始状态,和计算后缀 \(r+1\) 的起始状态是完全相同的。

第四步

现在复杂度是 \(O(n^2)\) 的(每次重新算后缀答案),考虑怎么快速计算 \(\mathrm{ans}_i\)。

经典考虑 \((a_i, b_i)\) 对所有后缀 \(1,2,\dots,i\) 的贡献。发现贡献和一个后缀 \(k\) 没什么屁关系,只和 \(s_{k}\) 有关系。

于是开一棵线段树,从 \(1\sim n\) 遍历后缀 \(k\):

  1. 先给 \(\mathrm{ans}_k\) 赋值为 \(-\mathrm{val}_{s_k}\),这里的 \(\mathrm{val}_p\) 指的是线段树上位置 \(p\) 的值。
  2. 再根据第二步添加贡献。
  3. 遍历完所有后缀后,查询 \(1\sim n\),令 \(\mathrm{ans}_k \gets \mathrm{ans}_k + \mathrm{val}_{s_k}\)。

关于添加贡献,会发现有个贡献和 \(|\Delta|\) 有关,可以拆绝对值,此时对于修改位置 \(i\),改变的量和 \(s_i\) 有关,可以直接记该位置 \(s_i\) 的系数,详见代码。

总时间复杂度 \(O(n\log n + q)\)。

什么?你说 \(O(n\log 4\cdot10^{14}+q)\)?不会离散化吗。

我的码呀

// Problem: F. MCMF?
// From: Codeforces - Codeforces Round #793 (Div. 2)
// URL: https://codeforces.com/contest/1682/problem/F
// Time: 2022-05-22 22:36
// Author: lingfunny

#include <bits/stdc++.h>
#define LL long long
using namespace std;
#define int LL	// 这是一个违背祖宗的决定(但我真找不出来哪没开 long long :(
const int mxn = 2e5+10, mod = 1e9+7;

int n, q, a[mxn], b[mxn], tot, rk[mxn], ans[mxn];
LL sa[mxn], s[mxn];

int mul() { return 1; }
template <typename... A>
int mul(int x, A... a) { return (LL)(x < 0 ? x + mod : x)*mul(a...)%mod; }
struct segtr {
#define mid ((L+R)>>1)
#define lc (o<<1)
#define rc (lc|1)
	int tag[mxn<<2], pre[mxn<<2];
    // pre: 系数 tag: 显式值
	inline void add(int o, int t) { (tag[o] += t) %= mod; }
	inline void adp(int o, int t) { (pre[o] += t) %= mod; }
	int ask(int p, int k, int o = 1, int L = 1, int R = tot) {
		if(L == R) return (tag[o] + mul(pre[o], s[k])) % mod;
		return ((LL)tag[o] + mul(pre[o], s[k]) + (p <= mid ? ask(p, k, lc, L, mid) : ask(p, k, rc, mid+1, R)))%mod;
	}
	void mdf(int cl, int cr, int k, int p, int o = 1, int L = 1, int R = tot) {
		if(cl <= L && R <= cr) return p ? add(o, k) : adp(o, k);
		if(cl <= mid) mdf(cl, cr, k, p, lc, L, mid);
		if(cr > mid) mdf(cl, cr, k, p, rc, mid+1, R);
	}
} T;

signed main() {
	scanf("%lld%lld", &n, &q);
	for(int i = 1; i <= n; ++i) scanf("%lld", a+i);
	for(int i = 1; i <= n; ++i) scanf("%lld", b+i);
	for(int i = 1; i <= n; ++i) sa[i] = s[i] = s[i-1] + b[i-1];
	sort(sa+1, sa+n+1); tot = unique(sa+1, sa+n+1) - sa - 1;
	for(int i = 1; i <= n; ++i) rk[i] = lower_bound(sa+1, sa+tot+1, s[i]) - sa;
	for(int i = 1, L, R; i <= n; ++i) {
		ans[i] = (mod - T.ask(rk[i], i)) % mod;
		L = R = -1;
		if(b[i] > 0) {
			L = rk[i], R = lower_bound(sa+1, sa+tot+1, b[i]+s[i]) - sa;
			// [1, L] -ab
			// (L, R) 2as[l] - 2as[i] - ab
			// [R, tot] ab
			T.mdf(1, R - 1, mod - mul(a[i], b[i]), 1);
			if(R <= tot) T.mdf(R, tot, mul(a[i], b[i]), 1);
			if(R - L > 1) T.mdf(L+1, R-1, mod - mul(2, a[i], s[i]), 1), T.mdf(L+1, R-1, mul(2, a[i]), 0);
		} else if(b[i] < 0) {
			b[i] = -b[i];
			L = upper_bound(sa+1, sa+tot+1, s[i]-b[i]) - sa - 1, R = rk[i];
			// [R, tot] -ab
			// (L, R) 2as[i] - 2as[l] - ab
			// [1, L] ab
			T.mdf(L+1, tot, mod - mul(a[i], b[i]), 1);
			if(L >= 1) T.mdf(1, L, mul(a[i], b[i]), 1);
			if(R - L > 1) T.mdf(L+1, R-1, mul(2, a[i], s[i]), 1), T.mdf(L+1, R-1, mod - mul(2, a[i]), 0);
		}
	}
	for(int i = 1; i <= n; ++i) (ans[i] += T.ask(rk[i], i)) %= mod;
	while(q--) {
		int l, r; scanf("%lld%lld", &l, &r);
		printf("%lld\n", (ans[l] - ans[r+1] + mod) % mod);
	}
	return 0;
}


这篇关于CF1682F 题解的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程