[ABC246G] Game on Tree 3 题解
前言
- 赛时沿用了 P4654 [CEOI2017] Mousetrap 的思路,成功导致想出了二分答案没想出 DP。
- 教练的思路很通俗易懂。
- 赛后讲题我自己的思路我自己都没懂,寄。
解题思路
本题解参照题目翻译,认为移动的一方为 B,另一方为 A。
容易发现答案具有单调性,考虑二分答案。
定义
为了方便,我们对点进行染色:在
定义
通俗易懂的解释是:如果 B 开局的时候下一步就要到达 作弊额外修改多少个节点的颜色才能使得 B 即使走到根为
其实不是很直观,如果感觉难以理解建议手玩一下,很好懂的。
接下来考虑转移方程:
其中
为什么是对的呢?
当 B 从点
而
因为 B 开局就在点
总时间复杂度
代码
#include<bits/stdc++.h>
using namespace std;
const int MAXN=200100;
long long val[MAXN],n,cpy[MAXN],dp[MAXN];
vector<long long> e[MAXN];
void check(long long x,long long tag,long long fat)
{
long long i,mp=e[x].size()-1,v,sum=0;
for(i=0;i<=mp;i++)
{
v=e[x][i];
if(v==fat)
{
continue;
}
else
{
check(v,tag,x);
sum+=dp[v];//求子节点的和
}
}
sum=max(sum-1,0ll);//为了避免减到 0 以下
dp[x]=sum+(val[x]>=tag);//判断是否为黑点
return;
}
int main()
{
long long i,j,ta,tb,l,r,mid,maxn=0,ans=0;
scanf("%lld",&n);
for(i=2;i<=n;i++)
{
scanf("%lld",&val[i]);
cpy[i]=val[i];
maxn=max(maxn,val[i]);
}
for(i=1;i<n;i++)
{
scanf("%lld%lld",&ta,&tb);
e[ta].push_back(tb);
e[tb].push_back(ta);
}
sort(cpy+1,cpy+1+n);
l=1;
r=n;
while(l<=r)//二分答案
{
mid=(l+r)/2;
check(1,cpy[mid],0);
if(dp[1]>=1)
{
ans=mid;
l=mid+1;
}
else
{
r=mid-1;
}
}
printf("%lld",cpy[ans]);
return 0;
}