The Game (Easy Version)
题目链接
题意:
给定一棵有n个节点的树,根节点为1,每个节点有一个值w[i]。Cirno和Daiyousei轮流选择节点,每次选择的节点值必须大于对手上次选择的节点值,并删除其子树。无法操作的一方胜利。你需要找到一个让Cirno获胜的初始节点。
思路:
首先假设你选了一个节点,删除其子树之后,所剩下的节点的权值的最大值都要小于等于该节点的权值时,Daiyousei肯定无法操作了,则Cirno获胜,故我们要选择的节点必须具备如下特性:删除该节点的子树后,所剩下的节点的权值的最大值大于该节点的权值。然后我们要从中选择一个符合题意的节点(一个就好)。考虑所有的节点中,人之常情,我们先从次大权值的节点开始筛选,因为最大的肯定不行,如果次大的有符合特性的,则说明另一方可以选择最大的,那么轮到我们的时候我们获胜;如果次大的都不符合特性的话,说明答案肯定就不是次大的了;则再选次次大的,如果有符合特性的,说明另一个人选的不是最大的就是次大的,等他选完之后,到我们的时候肯定就没有符合特性了(上面推出来的)。所以我们可以推出,要从所有符合上述特性的节点中选择一个权值最大的,它就是答案。至于如何判断一个节点是否符合特性看代码细节。
点击查看代码
#pragma GCC optimize("O3")
#pragma G++ optimize("O3")
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define ll long long
const int N = 4e5+5;
vector<int>v[N] ; //邻接表 存储树的边
int w[N]; //存储每个节点的值
bool vis[N]; //标记数组 用于dfs遍历时标记节点是否已访问
int id=0; //全局计数器 用于dfs编号
int x[N]; //x[i]表示i节点的在dfs时的编号
int y[N]; //y[i]表示dfs编号为i是的节点是什么
int maxid[N]; //maxid[i]表示在i节点及其子树的的最大dfs编号
void dfs(int u){
if(vis[u]) return ; //访问过就不必访问了
vis[u]=1; //打标记
x[u]=++id; //为其编号
y[id]=u; //将dfs编号对应的节点记录
for(auto i=v[u].begin();i!=v[u].end();++i){
dfs(*i); //遍历
}
maxid[u]=id; //在回溯时记录下u节点和其子树的最大dfs编号
}
int pre[N];
int hou[N];
void solve(){
int n;
cin>>n;//读取节点个数
id=0; //id先清零
for(int i=1;i<=n;++i){
cin>>w[i];
}
for(int i=0;i<=1+n;++i){
pre[i]=hou[i]=0; //先清零
}
for(int i=1;i<=n;++i){
vis[i]=0; //标记未访问过
v[i].clear(); //清空每一个节点所连向的点
}
for(int i=1;i<n;++i){
int a,b;
cin>>a>>b;
v[a].push_back(b); //连边
v[b].push_back(a);
}
dfs(1);
for(int i=1;i<=n;++i){
pre[i]=max(pre[i-1],w[y[i]]); //pre[i]表示dfs编号1-i的节点的最大权值
}
for(int i=n;i>=1;--i){
hou[i]=max(hou[i+1],w[y[i]]); //hou[i]表示dfs编号i-n的节点的最大权值
}
int mx=0;
for(int i=1;i<=n;++i){
if(max(pre[x[i]-1],hou[maxid[i]+1])>w[i]&&w[i]>w[mx]){ //pre[x[i]-1]表示在节点i之前的所有节点中的最大值
mx=i; //hou[maxid[i]+1]表示在节点i及其子树之后的所有节点中的最大值
// max(pre[x[i]-1],hou[maxid[i]+1])>w[i]表示该节点符合特性
}
}
cout<<mx<<endl; //输出答案
}
signed main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int _=1;
cin>>_;
while(_--)
solve();
return 0;
}

浙公网安备 33010602011771号