ABC246G Game on Tree 3 题解
ABC246G Game on Tree 3 Solution
更好的阅读体验戳此进入
题面
类似于 LG-P3554 [POI2013]LUK-Triumphal arch,给定一棵树,有点权,B 初始在 $ 1 $,每轮 A 选择一个点将权值变为 $ 0 $,然后 B 移动一次,B 可在任意时刻停止游戏然后获得所在点上的权值的得分,两人均采取最优策略那么最终 B 最少会拿到多少的得分。
Solution
与 LG-P3554 [POI2013]LUK-Triumphal arch 基本相同,对于本题依然考虑二分答案,对于当前的答案 $ k $,认为树上点权大于 $ k $ 的贡献为 $ 1 $,小于等于的为 $ 0 $,于是显然可以树形 DP,令 $ dp(i) $ 表示 A 在 $ i $ 节点上时需要额外多少次的变为 $ 0 $ 的操作,显然有转移 $ dp(i) = \max(\sum_{j \in son(i)} dp(j) - 1, 0) + \left[ v(i) \gt k \right] $,最后判断一下根节点 $ 1 $ 为 $ 0 $ 则合法,反之不合法。
对于二分的当然可以直接二分值域,不过这里可以考虑一个小剪枝,上界显然为点权的最小值,若根节点有多个子树,那么下界显然为根节点连接的节点的点权的最小值。
Code
#define _USE_MATH_DEFINES
#include <bits/extc++.h>
#define PI M_PI
#define E M_E
#define npt nullptr
#define SON i->to
#define OPNEW void* operator new(size_t)
#define ROPNEW(arr) void* Edge::operator new(size_t){static Edge* P = arr; return P++;}
using namespace std;
using namespace __gnu_pbds;
mt19937 rnd(random_device{}());
int rndd(int l, int r){return rnd() % (r - l + 1) + l;}
bool rnddd(int x){return rndd(1, 100) <= x;}
typedef unsigned int uint;
typedef unsigned long long unll;
typedef long long ll;
typedef long double ld;
template<typename T = int>
inline T read(void);
int N;
struct Edge{
Edge* nxt;
int to;
OPNEW;
}ed[410000];
ROPNEW(ed);
Edge* head[210000];
int val[210000];
int mnval(INT_MAX), mxval(-1);
int f[210000];
void dfs(int k, int p = 1, int fa = 0){
for(auto i = head[p]; i; i = i->nxt){
if(SON == fa)continue;
dfs(k, SON, p);
f[p] += f[SON];
}
f[p] -= 1;
f[p] = max(0, f[p]);
f[p] += val[p] > k ? 1 : 0;
}
bool Check(int K){
memset(f, 0, sizeof(f));
dfs(K);
return f[1] == 0;
}
int main(){
N = read();
for(int i = 2; i <= N; ++i)val[i] = read(), mxval = max(mxval, val[i]);
for(int i = 1; i <= N - 1; ++i){
int s = read(), t = read();
head[s] = new Edge{head[s], t};
head[t] = new Edge{head[t], s};
if(s == 1)mnval = min(mnval, val[t]);
if(t == 1)mnval = min(mnval, val[s]);
}if(!head[1]->nxt)mnval = 0;
int l = mnval, r = mxval;
int ans(-1);
while(l <= r){
int mid = (l + r) >> 1;
Check(mid) ? ans = mid, r = mid - 1 : l = mid + 1;
}printf("%d\n", ans);
fprintf(stderr, "Time: %.6lf\n", (double)clock() / CLOCKS_PER_SEC);
return 0;
}
template<typename T>
inline T read(void){
T ret(0);
short flag(1);
char c = getchar();
while(c != '-' && !isdigit(c))c = getchar();
if(c == '-')flag = -1, c = getchar();
while(isdigit(c)){
ret *= 10;
ret += int(c - '0');
c = getchar();
}
ret *= flag;
return ret;
}
UPD
update-2022_10_21 初稿