【模拟】CF 796C Bank Hacking
题目大意
洛谷链接
给定一棵带点权树,选出一个最佳的根节点,使得根节点的点权不变,它的儿子点权加1,其余点点权加2,并使最大点权最小,输出这个最小的最大点权。
其他见链接(懒)。
PS:原题面很不好总结题意,洛谷的翻译的确很清楚。
思路
先枚举最大点权设为\(v_{max}\)。
- 若最大点权的点只有一个
- 若没有\(v=v_{max}-1\)的点,答案即为\(v_{max}\)
- 若存在且为最大权值点的子节点,答案为\(v_{max}-1+1=v_{max}\)
- 若存在且不是最大权值点的子节点,答案为\(v_{max}-1+2=v_{max}+1\)
- 若最大点权的点不止一个
- 若其他最大点权的点都是一个最大点权的点的子节点,答案为\(v_{max}+1\)
- 若所有最大点权的点都是一个点的子节点,答案为\(v_{max}+1\)
- 若都不是,则答案为\(v_{max}+2\)
代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=300000+10;
const int INF=0x3f3f3f3f;
struct node{
int to,nxt;
}e[maxn*2];
int n,ans,vmax=-INF,a[maxn];
bool flag;
map<int,int> mp;//不能用数组!否则会RE,因为存在负权
int cnt,head[maxn];
void add(int x,int y){
e[++cnt].to=y;
e[cnt].nxt=head[x];
head[x]=cnt;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
mp[a[i]]++;//记录权值出现次数
vmax=max(vmax,a[i]);//找最大点权
}
ans=vmax+2;
for(int i=1;i<n;i++){
int x,y;
scanf("%d%d",&x,&y);
add(x,y);
add(y,x);
}
for(int i=1;i<=n;i++){
flag=0;
for(int j=head[i];j;j=e[j].nxt){
mp[a[e[j].to]]--;//儿子权值次数--
if(a[e[j].to]==vmax)flag=1;//flag为1表示儿子中有最大权值
}
if(!mp[vmax])ans=vmax+1;//所有最大权值点都是一个点的儿子
if(vmax==a[i]&&mp[vmax]==1){//最大值是根
if(mp[vmax-1])ans=vmax+1;//存在非儿子的权值为vmax-1的点,加2,得到的是vmax+1
else if(flag)ans=vmax+1;//儿子中有最大权值,加一就是最大了
else{
ans=vmax;//根是最大值
break;
}
}
for(int j=head[i];j;j=e[j].nxt)
mp[a[e[j].to]]++;//再加回来
}
printf("%d\n",ans);//以上都没有更新,就是vmax+2的情况
return 0;
}