【模拟】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;
}
posted @ 2020-04-30 09:55  Midoria7  阅读(115)  评论(0编辑  收藏  举报