← 全部文章

CF643D Bearish Fanpages 题解

本文目录(3 节)
  1. 题意
  2. 题解
  3. 代码

CF643D Bearish Fanpages

题意

  • 给定一个 nn 个点的基环森林,第 ii 个点与 fif_i 之间有一条边。
  • 保证在任意时刻,这个基环森林中的环长至少为 33。
  • 当一种「神秘事件」发生时,对于第 ii 个点,设与其相邻的点(包括 fif_i 和 fj=if_j = i 的 jj)的个数为 kk,这 kk 个点上都会出现 ⌊tik+1⌋\lfloor \frac{t_i}{k+1} \rfloor 个人,而 ii 上会出现 ti−k⋅⌊tik+1⌋t_i - k \cdot \lfloor \frac{t_i}{k+1} \rfloor 个人。
  • 每次「神秘事件」发生都是互不影响的。
  • 有 qq 个操作,操作有三种。
    1. 1 i j:将 fif_i 改为 jj,此操作不超过 5×1045 \times 10^4 个。
    2. 2 i:询问当「神秘事件」发生时,第 ii 个点上出现的人数。
    3. 3:询问当「神秘事件」发生时,所有点上出现的人数的最小值和最大值。
  • n,q≤105n,q \le 10^5,ti≤1012t_i \le 10^{12}。

题解

对于点 ii,将 fj=if_j = i 的所有 jj 的权值去掉 ii 的贡献后存到 ii 上的一个 multiset 里(记为 sis_i),然后 multiset 里的最大最小值加上 ii 的贡献存到一个大 multiset 里(记为 s0s_0)。

设 aia_i 表示 ii 去掉 fif_i 的贡献后的权值,did_i 表示 ii 的度数。

考虑每次修改,即将 fif_i 改为 jj 的过程:

  • 由于修改后 ii 不再对 fif_i 有贡献,afia_{f_i} 要减去修改前来自 ii 的贡献 ⌊tidi+1⌋\lfloor \frac{t_i}{d_i+1} \rfloor,sfis_{f_i} 中删除 aia_i。
  • 由于 dfid_{f_i} 修改后要减去 11,afia_{f_i} 要减去修改前自身的贡献 tfi−dfi⋅⌊tfidfi+1⌋t_{f_i} - d_{f_i} \cdot \lfloor \frac{t_{f_i}}{d_{f_i}+1}\rfloor,加上修改后自身的贡献 tfi−(dfi−1)⋅⌊tfidfi⌋t_{f_i} - (d_{f_i}-1) \cdot \lfloor \frac{t_{f_i}}{d_{f_i}}\rfloor,sffis_{f_{f_i}} 要更新 afia_{f_i}。
  • 由于 dfid_{f_i} 修改后要减去 11,affia_{f_{f_i}} 要减去修改前来自 fif_i 的贡献 ⌊tfidfi+1⌋\lfloor \frac{t_{f_i}}{d_{f_i}+1} \rfloor,加上修改后来自 fif_i 的贡献 ⌊tfidfi⌋\lfloor \frac{t_{f_i}}{d_{f_i}} \rfloor,sfffis_{f_{f_{f_i}}} 要更新 affia_{f_{f_i}}。

此时执行修改,即 dfid_{f_i} 减去 11,fif_i 改为 jj,djd_j 加上 11,然后将上面的过程反向重复一遍。

  • 由于修改前 ii 对 jj 没有贡献,aja_{j} 要加上修改后来自 ii 的贡献 ⌊tidi+1⌋\lfloor \frac{t_i}{d_i+1} \rfloor,sjs_{j} 中插入 aia_i。
  • 由于 djd_{j} 修改后加上了 11,aja_{j} 要减去修改前自身的贡献 tj−(dj−1)⋅⌊tjdj⌋t_{j} - (d_{j}-1) \cdot \lfloor \frac{t_{j}}{d_{j}}\rfloor,加上修改后自身的贡献 tj−dj⋅⌊tjdj+1⌋t_{j} - d_{j} \cdot \lfloor \frac{t_{j}}{d_{j}+1}\rfloor,sfjs_{f_{j}} 要更新 aja_{j}。
  • 由于 djd_{j} 修改后加上了 11,afja_{f_{j}} 要减去修改前来自 jj 的贡献 ⌊tjdj⌋\lfloor \frac{t_{j}}{d_{j}} \rfloor,加上修改后来自 jj 的贡献 ⌊tjdj+1⌋\lfloor \frac{t_{j}}{d_{j}+1} \rfloor,sffjs_{f_{f_{j}}} 要更新 afja_{f_{j}}。

注意每次更新 sis_i 的操作实质上是先删除再修改后插入,必须保证删除在修改之前,因此不能完全按照上面的顺序来做。

另外,别忘了每次更新 sis_i 还要同步更新 s0s_0,同时注意不能重复插入或删除。

所以实际上本题是个模拟题,总时间复杂度 O((n+q)log⁡n)\mathcal O((n+q) \log n)。

代码

const int N = 1e5 + 7;
int n, q, d[N], f[N];
bool v[N];
ll a[N], t[N];
multiset<ll> s[N];

inline void del(int x) {
	if (v[x] && s[x].size())
		v[x] = 0,
		s[0].erase(s[0].find((*s[x].begin()) + t[x] / (d[x] + 1))),
		s[0].erase(s[0].find((*(--s[x].end())) + t[x] / (d[x] + 1)));
}

inline void add(int x) {
	if (!v[x] && s[x].size())
		v[x] = 1,
		s[0].insert((*s[x].begin()) + t[x] / (d[x] + 1)),
		s[0].insert((*(--s[x].end())) + t[x] / (d[x] + 1));
}

inline void work(int i, int j) {
	del(f[i]), del(f[f[i]]), del(f[f[f[i]]]);
	del(j), del(f[j]), del(f[f[j]]);

	s[f[i]].erase(s[f[i]].find(a[i]));

	s[f[f[i]]].erase(s[f[f[i]]].find(a[f[i]]));
	a[f[i]] -= t[i] / (d[i] + 1);
	a[f[i]] -= t[f[i]] - d[f[i]] * (t[f[i]] / (d[f[i]] + 1));
	a[f[i]] += t[f[i]] - (d[f[i]] - 1) * (t[f[i]] / d[f[i]]);
	s[f[f[i]]].insert(a[f[i]]);

	s[f[f[f[i]]]].erase(s[f[f[f[i]]]].find(a[f[f[i]]]));
	a[f[f[i]]] -= t[f[i]] / (d[f[i]] + 1);
	a[f[f[i]]] += t[f[i]] / d[f[i]];
	s[f[f[f[i]]]].insert(a[f[f[i]]]);

	--d[f[i]], ++d[j];

	s[j].insert(a[i]);

	s[f[j]].erase(s[f[j]].find(a[j]));
	a[j] += t[i] / (d[i] + 1);
	a[j] -= t[j] - (d[j] - 1) * (t[j] / d[j]);
	a[j] += t[j] - d[j] * (t[j] / (d[j] + 1));
	s[f[j]].insert(a[j]);

	s[f[f[j]]].erase(s[f[f[j]]].find(a[f[j]]));
	a[f[j]] -= t[j] / d[j];
	a[f[j]] += t[j] / (d[j] + 1);
	s[f[f[j]]].insert(a[f[j]]);

	add(f[i]), add(f[f[i]]), add(f[f[f[i]]]);
	add(j), add(f[j]), add(f[f[j]]);

	f[i] = j;
}

int main() {
	rd(n), rd(q), rda(t, n), rda(f, n);
	for (int i = 1; i <= n; i++) ++d[i], ++d[f[i]];
	for (int i = 1; i <= n; i++)
		a[i] += t[i] - d[i] * (t[i] / (d[i] + 1)),
		a[f[i]] += t[i] / (d[i] + 1);
	for (int i = 1; i <= n; i++) s[f[i]].insert(a[i]);
	for (int i = 1; i <= n; i++) add(i);
	while (q--) {
		int o, i, j;
		rd(o);
		switch (o) {
			case 1 : rd(i), rd(j), work(i, j); break;
			case 2 : rd(i), print(a[i] + t[f[i]] / (d[f[i]] + 1)); break;
			case 3 : print(*s[0].begin(), ' '), print(*(--s[0].end())); break;
		}
	}
	return 0;
}

标签:基环树

评论

    填写昵称即可留言。评论经站主审核后公开,请勿提交私人信息。

    正在连接评论服务…