[ABC218G] Game on Tree 2 树上游戏

news/2024/11/22 19:18:53/

[ABC218G] Game on Tree 2 树上游戏

文章目录

  • [ABC218G] Game on Tree 2 树上游戏
    • 题面翻译
    • 输入格式
    • 输出格式
    • 样例 #1
      • 样例输入 #1
      • 样例输出 #1
    • 样例 #2
      • 样例输入 #2
      • 样例输出 #2
    • 样例 #3
      • 样例输入 #3
      • 样例输出 #3
    • 题目大意
    • 分析
      • 水法
        • code
      • 正解
        • code

题面翻译

给定一棵树,以及树各节点的点权(点权为偶数)。起初有一个棋子在树的根结点(结点 1 1 1)处。

  • A A A B B B 两人轮流操作:将棋子移动到其所在节点的某个叶子节点。
  • 到某个节点的得分定义为:棋子经过所有结点点权的中位数。 K K K 个数的中位数定义如下:
    • K K K 为奇数时,中位数为 K K K 个数中第 K + 1 2 \frac{K+1}{2} 2K+1 小的值。
    • K K K 为偶数时,中位数为 K K K 个数中第 K 2 \frac{K}{2} 2K 小与第 K 2 + 1 \frac{K}{2}+1 2K+1 小的两数的平均值。
  • A A A 希望最后得分尽可能大, B B B 希望最后得分尽可能小。
  • 当棋子到达某个叶节点时,游戏结束。

A A A 先手操作, A A A B B B 都采取最优策略,求最终得分。

输入格式

入力は以下の形式で標準入力から与えられる。

$ N $ $ A_1 $ $ A_2 $ $ \ldots $ $ A_N $ $ u_1 $ $ v_1 $ $ u_2 $ $ v_2 $ $ \vdots $ $ u_{N-1} $ $ v_{N-1} $

输出格式

両者が最適に行動するとき、駒が訪れた頂点に書かれた数の(多重)集合の中央値を出力せよ。

样例 #1

样例输入 #1

5
2 4 6 8 10
4 5
3 4
1 5
2 4

样例输出 #1

7

样例 #2

样例输入 #2

5
6 4 6 10 8
1 4
1 2
1 5
1 3

样例输出 #2

8

样例 #3

样例输入 #3

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

样例输出 #3

2

题目大意

一棵树上有 n n n 个点,每个点有一个点值。在顶点1处有一块令牌,现在小T和小J开始玩游戏,它们轮流移动令牌到一个邻接点,令牌不能两次经过同一个点,最后令牌不能移动为止。令牌移动过程中经过的点,将它们的权值加入一个数组,求该数组的中位数。注意:小T移动时,他会采取最佳策略使得最终的中位数最大,而小J刚好相反,他会使得最终中位数最小。两个人都按照最佳策略去移动令牌。问最后的中位数是多少。

分析

水法

考试时用了5分钟打了一个贪心的做法,就是每次最优策略只考虑下一层的情况,居然拿到了67分的好成绩。

code

#include <bits/stdc++.h>
#define fu(x, y, z) for (int x = y; x <= z; x++)
using namespace std;
const int N = 1e5 + 5;
int n, cnt, hd[N], a[N], ans[N], sum, fa[N];
struct node {int to, nt;
} e[N << 1];
void add(int u, int v) { e[++cnt].to = v, e[cnt].nt = hd[u], hd[u] = cnt; }
void dfs(int x, int y) {int flg = 0, max1 = 0, min1 = 1e9 + 5, v, j, k;for (int i = hd[x]; i; i = e[i].nt) {v = e[i].to;if (fa[x] == v)continue;flg = 1;if (max1 < a[v])max1 = a[v], j = v;if (min1 > a[v])min1 = a[v], k = v;}if (!flg)return;if (y) {fa[j] = x;ans[++sum] = a[j];dfs(j, y ^ 1);} else {fa[k] = x;ans[++sum] = a[k];dfs(k, y ^ 1);}
}
void solve() {sort(ans + 1, ans + sum + 1);if (sum % 2 == 1)printf("%d", ans[sum / 2 + 1]);elseprintf("%d", ans[sum / 2] + ans[sum / 2 + 1] >> 1);
}
int main() {scanf("%d", &n);fu(i, 1, n) scanf("%d", &a[i]);int u, v;fu(i, 1, n - 1) {scanf("%d%d", &u, &v);add(u, v), add(v, u);}ans[++sum] = a[1];dfs(1, 1);solve();
}

正解

观察一下我们可以发现,每个叶子节点代表一条路径,我们可以先用 D F S DFS DFS 统计出每个叶子结点的路径,并求出中位数,然后模拟。

好像会超时

我们发现可以用两个 multiset 或者对顶堆来维护这一条路径。

对顶堆 不会的可以看一下 这个

如果两个 m u l t i s e t multiset multiset 长度相等,那么中位数就是第一个数列加上第二个数列再除以二

否则就是第一个数列的最后一个数。

code

#include <bits/stdc++.h>
#define fu(x , y , z) for(int x = y ; x <= z ; x ++)
#define LL long long
using namespace std;
const int N = 1e5 + 5;
int n , cnt , hd[N] , a[N] , sum , fa[N] , max1[N] , min1[N] , ans1 , f[N] , ans[N];
struct node {int to , nt;
} e[N << 1];
multiset<int> s1 , s2;
void add (int u , int v) { e[++cnt].to = v , e[cnt].nt = hd[u] , hd[u] = cnt; }
void dfs (int x , int fa) {bool flg = 0;if (x == 1) s1.insert(a[x]);else {auto t = s1.end();t --;if (a[x] <= *t) {if (s1.size() == s2.size()) s1.insert(a[x]);else {s1.insert(a[x]);auto y = s1.end();y --;s2.insert(*y) , s1.erase(y);}} else {if (s1.size() == s2.size()) {s2.insert(a[x]);auto y = s2.begin();s1.insert(*y) , s2.erase(y);}else s2.insert(a[x]);}}for (int i = hd[x] ; i ; i = e[i].nt) {if (e[i].to == fa) continue;flg = 1;dfs (e[i].to , x);}if (!flg) {int len = s1.size() + s2.size();if (len & 1) {auto t = s1.end();t --;f[x] = *t;}else {auto a = s1.end();a--;auto b = s2.begin();f[x] = *a + *b >> 1;}}if (x != 1) {auto t = s1.end ();t --;if (*t < a[x]) s2.erase(s2.lower_bound(a[x]));else s1.erase(s1.lower_bound(a[x]));if (s1.size() < s2.size()) {auto y = s2.begin();s1.insert(*y) , s2.erase(y);}if (s1.size() - s2.size() == 2) {auto y = s1.end();y --;s2.insert(*y) , s1.erase(y);}}
}
void solve (int x , int fa , int flg) {int res;if (!flg)  res = -1;else res = 1e9 + 5;int y , bl = 0;for (int i = hd[x] ; i ; i = e[i].nt) {y = e[i].to;if (y == fa) continue;bl = 1;solve (y , x , flg ^ 1);if (!flg) res = max (res , ans[y]);else res = min (res , ans[y]);}if (bl) ans[x] = res;else ans[x] = f[x];
}
int main () {scanf ("%d" , &n);fu (i , 1 , n) scanf ("%d" , &a[i]);int u , v;fu (i , 1 , n - 1) {scanf ("%d%d" , &u , &v);add (u , v) , add (v , u);}dfs (1 , 0);solve (1 , 0 , 0);printf ("%d" , ans[1]);return 0;
}

http://www.ppmy.cn/news/796818.html

相关文章

golang系列-golang的25个关键字

1 关键字列表 break default func interface select case defer go map struct chan else goto package switch const fallthrough if range type continue for import return v…

岩板铺地好吗_岩板铺客厅地面好吗 比800*800的瓷砖更美观又大气?

现在大多数家庭新房装修时,客厅地面选择铺贴800mm*800mm规格大小的瓷砖,随着人们生活品质的不断提高,现在很多家庭对装修要求特别高,尤其是客厅区域,选用800mm*800mm的瓷砖不仅缝隙多,而且容易藏污纳垢,后期难清理不说,而且还不美观显得不够大气,降低了整体装修档次。…

webgl绘制客厅房间的家具+座椅板凳床

一、开发环境说明 开发软件&#xff1a;webstorm浏览器 &#xff1a; 火狐firefox编程语言&#xff1a;webgl 二、内容说明 1、内容要求 实现一个小型的3D客厅环境&#xff0c;包括对象建模、照明、摄像机设置&#xff1b;必须使用纯的webgl来实现&#xff0c;不能使用其他高…

客厅智能化(1、2)

对绝大多数朋友而言&#xff0c;客厅也许只是一个与客人会面的地方&#xff0c;只是一家人围坐在一起看电视、嗑瓜子、聊天的场所&#xff0c;但现在这一切正在悄然发生着变化。你家的客厅会慢慢变成一个超级大影院&#xff0c;不仅能看大片&#xff0c;你还能走进大片和里面的…

我用50行代码居然「让天猫精灵把客厅灯开了」

Author&#xff1a;AXYZdong 自动化专业 工科男 有一点思考&#xff0c;有一点想法&#xff0c;有一点理性&#xff01; 定个小小目标&#xff0c;努力成为习惯&#xff01;在最美的年华遇见更好的自己&#xff01; CSDNAXYZdong&#xff0c;CSDN首发&#xff0c;AXYZdong原创 …

Opengl+glfw+glew 大作业 绘制房间卧室客厅+雪花雪人

一、开发环境说明 操作系统&#xff1a;windows开发软件&#xff1a;Visual Studio 2017编程语言&#xff1a;基于控制台下的opengl用到的库&#xff1a; glfw 、glew底部提供代码下载 opengl环境配置可参照上一篇博客opengl环境配置GLFWGLEWVS2017 二、运行效果展示如下 三、…

树莓派串流客厅电视玩游戏

前段时间想入手一个steamlink享受在客厅用电视玩steam的快感&#xff0c;但是由于穷逼学生一枚&#xff0c;买不起&#xff01;&#xff01;&#xff01;只好自己DIY一个&#xff0c;正好家里有个树莓派&#xff0c;开始折腾&#xff01;&#xff01;&#xff01; 最初在网上找…

小米盒子 smb Android,客厅里的多媒体 小米盒子SMB本地连接

客厅里的多媒体 小米盒子SMB本地连接 2013年07月01日 15:02作者&#xff1a;厂商投稿文章出处&#xff1a;泡泡网原创 分享 泡泡网手机频道7月1日 还未在电脑上操作繁琐步骤来实现小米盒子的SMB么?小编教你只需在PC端设定分享影片、音乐及图片&#xff0c;就可以在你的小米盒子…