[ABC218G] Game on Tree 2 树上游戏

[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.hqwc.cn/news/12911.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Vue 之 mixins 和 provide/inject

一、mixins 1、简介 ​ mixins 又称 混入&#xff0c;是指将一些可复用的代码&#xff08;JS、生命周期钩子函数等等&#xff09;抽离出来&#xff0c;定义成mixins模块&#xff0c;然后混入到多个组件中&#xff0c;从而实现组件间的逻辑代码共享&#xff0c;减少重复代码。…

Web安全——数据库mysql学习

数据库mysql基础 Web安全分享一、数据库的基本操作1、MYSQL登录与退出2、MYSQL数据库的一些解释3、MYSQL注释符有三种&#xff1a; 二、数据库的一些基本操作1、数据库的增删改查(sql语句) 三、table 表的操作1、查看表结构2、查看表的内容3、建立表4、约束条件5、修改表的操作…

macOS Sonoma 14 beta 3 (23A5286g) Boot ISO 原版可引导镜像下载

macOS Sonoma 14 beta 3 (23A5286g) Boot ISO 原版可引导镜像&#xff0c;7 月 5 日&#xff08;北京时间今日凌晨&#xff09;已发布 本站下载的 macOS 软件包&#xff0c;既可以拖拽到 Applications&#xff08;应用程序&#xff09;下直接安装&#xff0c;也可以制作启动 U…

GaussDB云数据库SQL应用系列—分区表管理

目录 前言 一、分区表基本原理 二、分区表主要优势 三、分区表常见场景 四、GaussDB分区表管理&#xff08;示例&#xff09; 示例一&#xff1a;创建范围分区表(RANGE) 示例二&#xff1a;创建哈希分区表&#xff08;HASH&#xff09; 示例三&#xff1a;创建列表分区…

vue3 实现多层级列表

文章目录 需求背景解决效果index.vue视频效果 需求背景 需要在统一个列表下&#xff0c;实现商品和规格得管理和联动 解决效果 index.vue <!--/*** author: liuk* date: 2023/7/7* describe: 商品列表 */--> <template><div class"container">&…

Java类加载深度剖析-大白话

Java类加载深度剖析 1.类加载的入口2.AppClassLoader、ExtClassLoader、BootstrapClassLoader的血脉渊源3.ExtClassLoader究竟是不是孙大圣4.为什么自定义类加载器的父类加载器是AppClassLoader呢&#xff1f;5.我们应该如何打破双亲委派机制呢&#xff1f;6.如何保证同class对…

Mac VSCode配置运行单个C++文件

题外话&#xff1a;VSCode一键整理代码快捷键&#xff1a;ShiftoptionF 方法一&#xff1a;命令行直接编译 g -o 想创建的可执行文件名 ./cpp文件名 ./可执行文件名 以test.cpp为例&#xff0c;我创建的可执行文件名为test&#xff0c;运行结果如下&#xff1a; 方法二&#…

【裸机开发】GPT 定时器(一) —— GPT的功能、寄存器解析

后续需要使用 GPT 计数器实现中断以及延时&#xff0c;这里我们需要先了解一下GPT的功能以及相关寄存器。 目录 一、GPT 定时器的功能 1、计数器 2、输入捕获 3、输出比较&#xff08;GPT的两种工作模式&#xff09; 二、寄存器解析 1、GPTx_CR 2、GPTx_PR 3、GPTx_SR …

3.1.cuda运行API-概述

目录 前言1. Runtime API概述总结 前言 杜老师推出的 tensorRT从零起步高性能部署 课程&#xff0c;之前有看过一遍&#xff0c;但是没有做笔记&#xff0c;很多东西也忘了。这次重新撸一遍&#xff0c;顺便记记笔记。 本次课程学习精简 CUDA 教程-Runtime API 概述 课程大纲可…

【编程的多线程学习-前章】什么是进程,PCB进程控制块抽象,cup分配,内存分配,虚拟地址,进程的通信,进程的意义

什么是进程 什么是进程/任务&#xff08;Process/Task&#xff09;进程就是一个运行起来的程序PCB 进程控制块抽象(PCB Process Control Block)pcb就是一个进程PCB具体包含的信息 CPU 分配 —— 进程调度&#xff08;Process Scheduling&#xff09;内存分配 —— 内存管理&…

简单的手机记事本app怎么查看提醒列表?

很多人平时都有随手记事的习惯&#xff0c;在记录事情的时候使用手机上的记事本app是一个不错的选择。有的记事本功能比较完善&#xff0c;不但能记事还能设置提醒&#xff0c;当有多条提醒内容存在时&#xff0c;简单的手机记事本app怎么查看提醒列表呢&#xff1f;以iPhone手…

数据结构--线索二叉树找前驱后继

数据结构–线索二叉树找前驱后继 中序线索二叉树找中序后继 在中序线索二叉树中找到指定结点*p的 中序后继 \color{red}中序后继 中序后继next ①若p->rtag 1&#xff0c;则next p->rchild ②若p->rtag 0 中序遍历――左根右 左根(左根右) 左根((左根右)根右) next …