CodeForces 1292C Xenon's Attack on the Gangs

Description

描述

给一个 $n$ 个点的树,要求你将 $0 \sim n - 2$ 不重不漏的放在这 $n - 1$ 条边上,求 $S = \sum\limits_{1 \le u < v \le n} \operatorname{mex}(u, v)$ 的最大值,$\operatorname{mex}(u, v)$ 表示 $<\! u \to v \!>$ 的路径上所经过的边权集合中最小的没出现的非负数。

输入

第一行一个正整数 $n$($2 \le n \le 3000$)。

接下来 $n - 1$ 行,每行两个数 $u, v$,表示一条边($1 \le u, v \le n$,$u \neq v$)。

输出

一个数 $S$ 表示答案。

样例

输入1

3
1 2
2 3

输出1

3

输入2

5
1 2
1 3
1 4
3 5

输出2

10

解释

样例1:

  • $\operatorname{mex}(1,2)=0$
  • $\operatorname{mex}(1,3)=2$
  • $\operatorname{mex}(2,3)=1$

所以 $S = 0 + 2 + 1 = 3$。

样例2:

  • $\operatorname{mex}(1, 3) = 1$
  • $\operatorname{mex}(1, 5) = 2$
  • $\operatorname{mex}(2, 3) = 1$
  • $\operatorname{mex}(2, 5) = 2$
  • $\operatorname{mex}(3, 4) = 1$
  • $\operatorname{mex}(4, 5) = 3$

所以 $S = 1 + 2 + 1 + 2 + 1 + 3 = 10$。

Solution

观察一下答案式子:

$$ \large
\begin{array}{rl}
S = & \!\! \sum\limits_{1\le u<v \le n} \operatorname{mex}(u, v) \\
= & \!\! \sum\limits_{x = 1}^{n} \left( \sum\limits_{\operatorname{mex}(u, v) = x} x \right) \cdots\cdots(1)\\
= & \!\! \sum\limits_{x = 1}^{n} \left( \sum\limits_{\operatorname{mex}(u, v) \ge x} 1 \right)\cdots\cdots(2)
\end{array}$$

$(1) \Rightarrow (2)$ 是怎么推的呢?

考虑一个 $\operatorname{mex}(u, v)$,比如它等于 $y$,原本它对答案只产生一次为 $y$ 的贡献;现在,它对于 $\forall 1\le x\le y$ 都会产生 $1$ 的贡献,正好和还是 $y$。

于是我们定义一个函数 $\operatorname{F}()$,$\operatorname{F}(x) = \sum_{1 \le u < v \le n}[\operatorname{mex}(u, v) \ge x]$。那么答案式子又可以化为:

$$ \large \begin{array}{rl} S = & \!\! \sum\limits_{x = 1}^{n} \left( \sum\limits_{\operatorname{mex}(u, v) \ge x} 1 \right)\cdots\cdots(2) \\
= & \!\! \sum\limits_{x = 1}^{n} \operatorname{F}(x)
\end{array} $$

$\operatorname{F}(x)$ 说通俗一点,就是 $< \! u \to v \! >$ 恰好包含了 $0 \sim x - 1$ 的所有数的路径数量。

现在我们从 $0$ 开始,依次放每一个数。比如现在我们要放 $x$,$0 \sim x - 1$ 已经放好了,那么我们一定会把 $x$ 和它们放在同一条路径上,要不然 $x$ 放了以后对答案没有影响。这是因为,放在同一条路径上的话,这里的 $\operatorname{mex}$ 就要变成 $x+ 1$ 了;否则 $0 \sim x - 1$ 已经有数字缺失,那就保持那个缺失的最小数字不变。

那么我们就可以找一个 $<\!u \to v\!>$,它的长度为 $l$,我们要把 $0 \sim l - 1$ 都放在这一条路上。$l \sim n - 2$ 放的位置我们不管,因为它们不会使得答案变劣,更优的方法在后面也一定能枚举到。

想一想,一个最佳的方案一定不仅是完整的一段,而且它的一部分也要是完整的。

比如我们现在选了 $<\!u \to v\!>$:

然后 $0$ 我们随便放一下:

下面我们要放 $1$,为了利益最大化,显然,$1$ 和 $0$ 要在一起(这样我们在构造 $0 \sim l - 1$ 的同时也顺便构造了一个 $0 \sim 1$ 的路径)。

比如 $1$ 放在左边:

然后我们放 $2$,这时我们不管放在 $1$ 的左边还是 $0$ 的右边,都可以顺便构成 $0 \sim 2$ 的路径。

依次类推放完:

发现了什么?从 $u$ 到 $v$ 依次写下来,正好是一个 单谷序列,也就是比如把这个序列叫做 $a$,则有一个位置 $p$,使得 $a_1 > a_2 > \cdots > a_p < \cdots < a_{l-1} <a_l$。

  • 用 $dp(u, v)$ 表示把 $0 \sim l - 1$ 放在 $<\!u \to v\!>$ 上,$\sum_{i =1}^{l} \operatorname{F}(i)$ 的最大可能值;
  • 用 $s_{root, u}$ 表示以 $root$ 为根时,$u$ 为根的子树大小;
  • 用 $p_{root, u}$ 表示以 $root$ 为根时,$u$ 的父亲。

现在我们要计算 $dp(u, v)$,根据单谷序列的性质,$l - 1$ 要么在最左边,要么在最右边,那我们分类讨论一下:

  • 如果 $l - 1$ 放在最左边,那么剩下的部分就是 $dp(p_{v, u}, v)$ 的答案,而享受到 $0 \sim l - 1$ 的路径的个数就是 $\operatorname{F}(l) = s_{u, v} \times s_{v,u}$,所以 $dp(u, v) = dp(p_{v, u}, v) + s_{u, v} \times s_{v,u}$;
  • 如果 $l - 1$ 放在最右边,那么剩下的部分就是 $dp(u, p_{u, v})$ 的答案,而享受到 $0 \sim l - 1$ 的路径的个数依然是 $\operatorname{F}(l) = s_{u, v} \times s_{v,u}$,所以 $dp(u, v) = dp(u, p_{u, v}) + s_{u, v} \times s_{v,u}$。

综上所述:
$$\large dp(u, v) = \max(dp(u, p_{u, v}), dp(p_{v, u}, v)) + s_{u, v} \times s_{v,u} $$

我们可以记忆化搜索,$dp(u, v)$ 就可以 $\mathcal O(1)$ 求了,总时间复杂度就是枚举 $u, v$ 的 $\mathcal O(n^2)$。至于 $p_{root, u}$ 和 $s_{root, u}$,可以在之前通过枚举 $root$,每次 $\mathcal O(n)$ 预处理出来。总时间复杂度 $\mathcal O(n^2)$。

代码贴出,仅供参考:

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 3005;
int n, rt, p[N][N], s[N][N];
LL f[N][N], ans;
vector<int> G[N];
void build(int u)
{
	s[rt][u] = 1;
	for(int v : G[u]) if(v ^ p[rt][u])
	{
		p[rt][v] = u;
		build(v);
		s[rt][u] += s[rt][v];
	}
}
LL dp(int u, int v)
{
	if(u == v) return 0;
	if(f[u][v]) return f[u][v];
	return f[u][v] = max(dp(u, p[u][v]), dp(v, p[v][u])) + s[u][v] * s[v][u];
}
int main()
{
	ios::sync_with_stdio(false);
	cin >> n;
	for(int i = 1; i < n; i++)
	{
		int u, v;
		cin >> u >> v;
		G[u].push_back(v);
		G[v].push_back(u);
	}
	for(int i = 1; i <= n; i++) { rt = i; build(i); }
	for(int u = 1; u <= n; u++)
        for(int v = 1; v <= n; v++)
            ans = max(ans, dp(u, v));
	cout << ans << endl;
	return 0;
}

参考文献:
Akikaze.Codeforces Round #614 Editorial.CodeForces

CodeForces 1292C Xenon's Attack on the Gangs

原文地址:https://www.cnblogs.com/syksykCCC/p/CF1292C.html

时间: 2024-11-06 19:19:21

CodeForces 1292C Xenon's Attack on the Gangs的相关文章

Codeforces Round #614 (Div. 2) E. Xenon&#39;s Attack on the Gangs

On another floor of the A.R.C. Markland-N, the young man Simon "Xenon" Jackson, takes a break after finishing his project early (as always). Having a lot of free time, he decides to put on his legendary hacker "X" instinct and fight ag

【cf1293E】E.Xenon&#39;s Attack on the Gangs(dp)

传送门 题意: 给出一颗树,树上随机分配\(0\)到\(n-1\)的边权,不存在权值相同的两条边. 定义\(mex(u,v)\)为:树上\(u\)到\(v\)的简单路径中所有边权的\(mex\). 求 \[ \sum_{1\leq u\leq v\leq n}mex(u,v) \] 思路: 将问题转化为求一条边的贡献,显然一条边对跨过这条边的所有点对有贡献: 多条边时,只有链的形式才会增加贡献,可以不用考虑具体的权值分配: 因为数据范围只有\(3000\),考虑枚举每条链进行\(dp\). 记忆

CF1292C Xenon&#39;s Attack on the Gangs 题解

传送门 题目描述 输入格式 输出格式 题意翻译 给n个结点,n-1条无向边.即一棵树.我们需要给这n-1条边赋上0~ n-2不重复的值.mex(u,v)表示从结点u到结点v经过的边权值中没有出现的最小非负整数.计算下面等式的最大值: 样例 样例输入 3 1 2 2 3 样例输入一 5 1 2 1 3 1 4 3 5 样例输入二 样例输出 样例输出一 3 样例输出二 10 分析 我们先随便找一条边,将它的价值赋值成0 那么只要有一个路径经过这条边,那么这个路径的最小价值就一定不会为0 我们举一个例

[CF1292C] Xenon&#39;s Attack on the Gangs

前言 我水一次题解.我是真的真的真的没有时间了,昨晚高考课任务太多了,完全完成一道题就十点半了.这个看懂题意就贼费劲,在晚新闻之前真的没有时间写题了.水一次,真的就这一次,讲完之后一定补上.我谢罪! 题目 原题链接 解说 解说地址(晚自习一定补上自己的思路,谢罪!) 代码 1 #include<bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int maxn=3000+5; 5 int n,rt,p[m

CF1293E-Xenon&#39;s Attack on the Gangs 树状DP

0边把图分成两个部分,这两个部分的路径之间,mex起码为1,都对答案产生1的贡献. 然后1边接在0边旁边,把图分成了更小的两个部分(0,1的两端),和一些不会再产生新的贡献的区域,这两个更小的部分路径之间,mex起码为2,都对答案又产生了1的贡献.(他们在刚刚算mex起码为1的时候,已经贡献过1了,所以再贡献1,mex就起码为2了). 依次类推,我们发现最后只有一条链上的边放置的是有意义的,其他边都可以随便放,反正不会产生贡献. 我们考虑这种情况下如何求解. 如果只考虑一条链,dp[i][j]从

Codeforces Round #614 (Div. 2)

A. ConneR and the A.R.C. Markland-N 题目链接:https://codeforces.com/contest/1293/problem/A 题意: 有一个长为 n 的楼层,其中有 k 个楼层没有餐厅 ,你现在在 s 层,问你最少走多少个楼层可以到达餐厅吃饭 分析: 因为 k 只有 1000,所以直接往 s 层上下方找(当找到 0 或者 n + 1 时说明这个方向没有答案) #include<bits/stdc++.h> using namespace std;

Codeforces Round #614

NEKO's Maze Game 题意 题解 代码 Aroma's Search 题意 题解 代码 Xenon's Attack on the Gangs 题意 题解 代码 NEKO's Maze Game 题目链接 https://codeforces.com/contest/1292/problem/A 题意 给出一个 2xN 的地图,每一时刻都有一个位置翻转状态(可走和不可走变换),输出每时刻是否可以从起点走到终点. 题解 地图只有 2xN,两个跨行相邻的位置不可走从起点就走不到终点. [

Codeforces 701B. Cells Not Under Attack

Vasya has the square chessboard of size n?×?n and m rooks. Initially the chessboard is empty. Vasya will consequently put the rooks on the board one after another. The cell of the field is under rook's attack, if there is at least one rook located in

codeforces #364b Cells Not Under Attack

比赛的时候 long long sum=n*n,计算不出1e10长度到数,没有搞掉. 哎,以后要注意这个地方.这个题其实不难: 统计能被攻击到的个数,然后用总的个数减掉就可以了.注意有些地方重复计算,要给去掉. B. Cells Not Under Attack time limit per test 2 seconds memory limit per test 256 megabytes input standard input output standard output Vasya ha