0%

CF-1077-E. Jerry and Tom(没有思路的时候,可以从特殊情况入手)(可以从分析单步最优入手,从局部最优到整体最优)(不符合单步最优的,我们直接删除)(太复杂了,就要想想是不是可以删一些东西,即边)(深度其实也只和儿子的数量有关系)

题目大意

Jerry和Tom正在一个有nn个顶点的有向图GG上玩一个游戏,顶点编号从11nn。对于每个顶点1u<n1 \le u < n,从uuu+1u+1有一条有向边。此外,还有mm条额外的有向边。第ii条额外边从uiu_iviv_i

GG具有以下特殊性质:不存在两条有向边(uivi)(u_i\to v_i)(ujvj)(u_j\to v_j)使得ui<uj<vi<vju_i < u_j < v_i < v_j

在游戏开始时,Jerry和Tom分别站在顶点xxyy上,其中xyx \ne y。游戏按照轮次进行。在每个轮次中,玩家根据以下规则行动,Jerry先行,然后是Tom:

  • Jerry 必须选择从他当前顶点出发的一条出边****并沿着它移动到终点。如果他当前顶点没有出边,则他保持原地

  • Tom 可以选择从他当前顶点出发的一条出边并沿着它移动到终点,或者选择不移动并保持原地。

如果在任何轮次结束时,Jerry和Tom在同一个顶点上(包括在顶点nn),游戏立即结束且Tom获胜。否则,如果Jerry最初在顶点nn,或在任何轮次结束时到达顶点nn,Jerry获胜。

注意,如果在一轮之后,Jerry和Tom都在顶点nn,则Tom获胜

在整个游戏过程中,两个玩家都知道对方的位置。

可以证明游戏会在有限轮次内结束。

对于一对整数1x,yn1 \le x,y \le nxyx \ne y,定义f(x,y)f(x,y)如下:

  • Jerry和Tom将进行一个游戏,其中Jerry从顶点xx开始,Tom从顶点yy开始。Tom想赢,但他也想最小化他实际移动的次数(即,他改变顶点的轮次数;原地不算移动)。假设双方都以最佳方式行动,如果Tom不能赢则令f(x,y)=0f(x,y)=0;否则,令f(x,y)f(x,y)为Tom强迫赢所需的最小移动次数。如果Tom在最佳游戏中获胜,Jerry仍会尽力最大化Tom必须移动的次数。

计算

1x,yn,xyf(x,y). \sum\limits_{1 \le x,y \le n, x \ne y}f(x,y).

思路讲解

我们梳理一下题意,并进行一下样例解释

image

题目不允许这样子的两条边存在。

image

在样例 2 中,贡献为 1 的两种情况。

如果说没有额外的边,那么答案就是 00。因为 Tom 在 Jerry 前面,反正肯定赢不了。Tom 在 Jerry 后面,守株待兔即可。可以发现,无论什么情况,根据定义,都是 00

我们现在这个问题还是回来,什么时候肯定不会有这个追及,有这个贡献?其实就是如果把额外边当作区间加法的话,Tom 在没有被任何区间覆盖的地方,肯定是不会有答案贡献的。

不过我们发现,这样子一种一种情况看,实在是太慢了。既然是博弈题目,那么Tom 和 Jerry 的最优策略是什么呢?就是每步都走最远的边

因此,我们不妨把 Tom 走的路径,以及 Jerry 走的路径绘画在一张图上(按照上面说的最优策略),这张图,其实是一颗树。

或者,换句话说,我们只保留每个点的最长出边(这样子就可以形成一颗树),较短的出边,我们直接删除(因为不符合)。

接着就是来处理这个贡献与计算问题了。

我们想要计算的就是,当我们已经知道 一个点 uu,在其他所有树上,比他的深度浅或者同样深度的所有点,记作 VV 集合,贡献为:

vVdep(u)dep(lca(u,v))\sum_{v\in V}\text{dep}(u)-\text{dep}(\text{lca}(u,v))

这个问题,有许多解决方法,不过,我们这里选择使用启发式合并。

启发式合并的关键就是,合并过程复杂度必须要依赖于轻儿子的这个子树的参数(如节点数量,深度等),不能够依赖于遍历重儿子。

不过,这个要求并不难,使用前缀和,搞一搞,就好了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// 计算答案,把轻儿子节点合并进主集合中。
for (auto [dep_son,cnt]:node_dep_cnt_mtx[to]) {
// cnt 是深度为 dep_son,较大的节点的数量(Jerry),get_presum_val 拿到的
// 是所有比 dep_son 小的点的 dep(v)-dep(lca(u,v)) 的这个和
ans+=cnt*get_presum_val(dep_son);
// 深度相同的节点可以反复贡献(可以理解为上面也可以贡献,下面也可以贡献)
ans+=cnt*node_dep_cnt_mtx[u][dep_son]*(dep_son-dep);
// 这个是深度比 dep_son 大的节点的数量
ll num_gt_dep=sum_node-get_presum_num(dep_son);
// 深度比 dep 深度深的节点,也会贡献,因为已经在重儿子中的深度深的节点
// 合并进来的时候还没有这样一个节点,贡献的就是 dep_son-dep(lca)
ans+=num_gt_dep*cnt*(dep_son-dep);
// 这里是顺手进行一个更新
node_dep_cnt_mtx[u][dep_son]+=cnt;
}

注意这个有可能出现重儿子比轻儿子的这个链长短的这个情况(当然可以变为分长儿子/短儿子)。这个时候直接取这个前缀和,因为我们用 map 实现,就会取到 00。这个时候可以用一个匿名 get 函数特判一下这个情况。

这种情况的一个 hack 数据:

1
2
3
4
5
6
1
7 2
3 7
4 6

ans:20

AC代码

AC
https://codeforces.com/contest/2188/submission/361002651

心路历程(WA,TLE,MLE……)