[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;
}