首页 > 其他分享 >CodeForces 1856E2 PermuTree (hard version)

CodeForces 1856E2 PermuTree (hard version)

时间:2023-08-06 09:02:20浏览次数:34  
标签:sz typedef 背包 log int PermuTree hard CodeForces define

洛谷传送门

CF 传送门

考虑局部贪心,假设我们现在在 \(u\),我们希望 \(u\) 不同子树中的 \((v, w), a_v < a_u < a_w\) 的对数尽量多。

我们实际上只关心子树内 \(a_u\) 的相对大小关系,不关心它们具体是什么。如果 \(u\) 只有两个儿子 \(v, w\),我们可以让 \(v\) 子树内的 \(a\) 全部小于 \(w\) 子树内的 \(a\),这样 \(u\) 作为 \(\text{LCA}\) 的贡献是 \(sz_v \times sz_w\),是最大的。

那么对于 \(u\) 有多个儿子的情况,推广可知相当于把 \(u\) 的儿子分成 \(S, T\) 两个集合,最大化 \(\sum\limits_{v \in S} sz_v \times \sum\limits_{v \in T} sz_v\)。考虑做一个 \(sz_v\) 的 01 背包,若能把 \(sz_v\) 分成大小为 \(x\) 的集合,\(u\) 对答案的贡献是 \(x \times (sz_u - 1 - x)\)。取这个的最大值即可。

01 背包暴力做即可,根据树形背包的那套理论,每个点对只会在 \(\text{LCA}\) 处被统计,所以时间复杂度 \(O(n^2)\),可以通过 E1。

对于 E2,我们肯定不能再暴力 01 背包了。发现我我们背包的复杂度跟 \(sz_v\) 有关。联想到 dsu on tree,轻子树的大小之和为 \(O(n \log n)\)。于是我们考虑将 \(u\) 的 \(sz\) 最大的两个儿子拎出来,剩下的儿子做一个背包,然后再枚举那两个儿子选不选。

至于如何做背包,我们把 \(sz_v\) 相同的物品看做一种有多个的物品,做单调队列优化多重背包即可。因为去掉两个最大子树后,\(sz_v\) 之和为 \(n \log n\),所以不同的 \(sz_v\) 有 \(O(\sqrt{n \log n})\) 种。

所以这么算下来复杂度其实是 \(O(n \sqrt{n \log n} \log n)\),但是它过了???

code
// Problem: E2. PermuTree (hard version)
// Contest: Codeforces - Codeforces Round 890 (Div. 2) supported by Constructor Institute
// URL: https://codeforces.com/contest/1856/problem/E2
// Memory Limit: 512 MB
// Time Limit: 3000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mkp make_pair
#define mems(a, x) memset((a), (x), sizeof(a))

using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<ll, ll> pii;

#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
char buf[1 << 21], *p1 = buf, *p2 = buf;
inline int read() {
    char c = getchar();
    int x = 0;
    for (; !isdigit(c); c = getchar()) ;
    for (; isdigit(c); c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
    return x;
}

const int maxn = 1000100;

int n, sz[maxn];
bool f[maxn], g[maxn];
ll ans;
vector<int> G[maxn];

void dfs(int u) {
	sz[u] = 1;
	vector<int> vc;
	for (int v : G[u]) {
		dfs(v);
		sz[u] += sz[v];
		vc.pb(sz[v]);
	}
	int m = (int)vc.size();
	if (m <= 2) {
		ll mx = 0;
		for (int S = 0; S < (1 << m); ++S) {
			int s = 0;
			for (int i = 0; i < m; ++i) {
				if (S & (1 << i)) {
					s += vc[i];
				}
			}
			mx = max(mx, 1LL * s * (sz[u] - 1 - s));
		}
		ans += mx;
		return;
	}
	sort(vc.begin(), vc.end(), greater<int>());
	int s = 0;
	for (int i = 2; i < m; ++i) {
		s += vc[i];
	}
	s /= 2;
	for (int i = 0; i <= s; ++i) {
		f[i] = 0;
	}
	f[0] = 1;
	for (int l = 2, r = 2; l < m; l = (++r)) {
		while (r + 1 < m && vc[r + 1] == vc[l]) {
			++r;
		}
		for (int i = 0; i <= s; ++i) {
			g[i] = f[i];
			f[i] = 0;
		}
		int c = r - l + 1, v = vc[l];
		for (int i = 0; i < v; ++i) {
			int cnt = 0, t = i;
			for (int j = i, k = 0; j <= s; j += v, ++k) {
				cnt += g[j];
				if (k > c) {
					cnt -= g[t];
					t += v;
				}
				f[j] = (cnt ? 1 : 0);
			}
		}
	}
	ll mx = 0;
	for (int i = 0; i <= s; ++i) {
		if (!f[i]) {
			continue;
		}
		for (int S = 0; S < 4; ++S) {
			int k = ((S & 1) ? vc[0] : 0) + ((S & 2) ? vc[1] : 0) + i;
			mx = max(mx, 1LL * k * (sz[u] - 1 - k));
		}
	}
	ans += mx;
}

void solve() {
	n = read();
	for (int i = 2, p; i <= n; ++i) {
		p = read();
		G[p].pb(i);
	}
	dfs(1);
	printf("%lld\n", ans);
}

int main() {
	int T = 1;
	// scanf("%d", &T);
	while (T--) {
		solve();
	}
	return 0;
}

标签:sz,typedef,背包,log,int,PermuTree,hard,CodeForces,define
From: https://www.cnblogs.com/zltzlt-blog/p/17609043.html

相关文章

  • CodeForces 1856D More Wrong
    洛谷传送门CF传送门直接求最大值不好求。我们可以采用一个交互常见的套路,维护可能的答案集合\(S\),每次剔除掉一些。考虑初始把\(a_{i-1}<a_i>a_{i+1}\)的\(i\)加入\(S\),每次取出\(S\)中差最小的一对元素\(i,j\),那么若\(a_i>a_j\),\(a_i\)就大于\([i+1......
  • Codeforces Round 882 (Div. 2)
    CodeforcesRound882(Div.2)ATheManwhobecameaGod给定一个数组\(\{x_1,x_2,\cdots,x_n\}\)和一个整数\(k\),记\(f(l,r)=\sum_{i=0}^{i\ler-l}|x_{l+i}-x_{l+i+1}|\),求将数组划分为\(k\)个部分的划分方案,使得对每个部分的\(f(l,r)\)之和最小.​ 将两数相......
  • Codeforces Round 885 (Div. 2) C. Vika and Price Tags
    C.VikaandPriceTagsC-VikaandPriceTags题意:​ 初始两串数列\(a,b\),对于第\(i\)个数,令\(c_i=|a_i-b_i|\),然后将数列\(a=b,b=c\)。重复这样的操作,问是否可能让\(a\)串全部变成0。思路:这题实际上之前已经发过,但最近打牛客对这题有了新的理解,所以来记录一下。​ ......
  • Codeforces 1843D:Apple Tree
    1843D.AppleTreeDescription:一棵树(\(Tree\)无环无重边)\(n\)个节点,根节点为1(节点编号\(1\)~\(n\)),树上只有2个苹果,每次摇动苹果树时,每个苹果会有如下变化:当前苹果位于节点\(u\):1.若节点\(u\)有子节点,则该苹果移动到此节点(若有多个子节点,则可以到任意一个)。2.......
  • sharding-jdbc配置
    一、概念先行1)SQL相关的逻辑表:水平拆分的数据库(表)的相同逻辑和数据结构表的总称。例:订单数据根据主键尾数拆分为2张表,分别是t_order_0到t_order_1,他们的逻辑表名为t_order。真实表:在分片的数据库中真实存在的物理表。例:示例中的t_order_0到t_order_1数据节点:数据分片的最小单......
  • Educational Codeforces Round 151
    EducationalCodeforcesRound151T1就是大水题但写了很长时间。构造题。首先分类讨论:当\(x\ne1\)时我们构造的序列长度就为\(n\),序列就是\(n\)个\(1\)。当\(x=1\)时,当\(n\)为偶数,我们就枚举\(1\simk\)且\(i\nex\),只要\(n\)能整除\(i\),长度为\(......
  • Practice on Codeforces and Atcoder in August
    EducationalCodeforcesRound151A~ECodeforcesRound#879Div.2CodeforcesRound#882Div.2CodeforcesRound#873Div.2......
  • Educational Codeforces Round 151 (Rated for Div. 2) 题解
    A.ForbiddenInteger显然,当\(x\not=1\)时,直接输出\(n\)个\(1\)即可否则,如果\(n\)为奇数,那就输出\(\lfloor\frac{n}{2}\rfloor-1\)个\(2\)和\(3\);如果\(n\)为偶数,那就输出\(\frac{n}{2}\)个\(2\)(要看一下\(k\)的大小)B.ComeTogether因为Bob和Carol都......
  • Codeforces Round 882 (Div. 2)
    link题号:CF1847A~FA题意:给定一个数组\(\{x_1,x_2,\cdots,x_n\}\)和一个整数\(k\),记\(f(l,r)=\sum_{i=0}^{i<r-l}|x_{l+i}-x_{l+i+1}|\),求将数组划分为\(k\)个部分的划分方案,使得对每个部分的\(f(l,r)\)之和最小.题解:简单题,首先我们注意到,如果将\(l,l+1\)隔开,那......
  • Codeforces Round 424 (Div. 1)D. Singer House
    传送门显然要自底向上进行\(dp\)深度相同的子树结构相同所以可以利用深度来代表子树。那么就应该统计出有向路径的个数。考虑路径由链所拼成。那么状态里应该有有向链的条数。设\(f_{i,j}\)表示深度为\(i\)链条数为\(j\)的方案数。不选当前的节点则\(f_{i,j}=f_{i+1,k}\cdot......