Codeforces Round #408 (Div. 2) C

43 篇文章 0 订阅
4 篇文章 0 订阅

http://codeforces.com/contest/796/problem/C

题意:给一颗树,删除节点,每次删除节点会使未被删除邻居和未被删除邻居的邻居权+1,问删除整棵树遇到最小的最大权是多少。
第一次以后的删除只能删除已被删除节点的邻居。

想要使答案尽量的小则优先选择权大的删除。
考虑到一个节点最多只能+2,假设ai最大值为mx,则答案可能为mx , mx +1 , mx +2。
而权为mx-1的节点最大权值可以达到mx+1,同样可以影响答案。

答案为mx的时候一定只有一个权为mx的节点 , 如果有多个则后面删除的初始权为mx的节点的权一定会改变 。 如果有权mx-1的节点则一定要与权为mx的节点相连,否则一定有权为mx-1的点权值+2。
这里写图片描述
这里写图片描述

接下来讨论答案为mx+1的情况,权为mx-1的节点可以创造的最大权值为mx+1所以可以忽略。权为mx的节点可以创造的最大价值为mx+2,所以研究关于权为mx的节点的限制条件。

若答案为mx+1,则每个权为mx的节点一定最多只+1了一次。
假设有x个权为mx的节点 则一定有1个节点与x-1个节点相连。
否则x个节点中一定有节点将会+2。
这里写图片描述

这里写图片描述

答案不为mx和mx+1时即答案为mx+2
然后对于每种情况做判断就行了

#include <iostream>
#include <algorithm>
#include <sstream>
#include <string>
#include <queue>
#include <cstdio>
#include <map>
#include <set>
#include <utility>
#include <stack>
#include <cstring>
#include <cmath>
#include <vector>
using namespace std;
#define pb push_back
#define scand(n) scanf("%d",&n)
#define scandd(n,m) scanf("%d%d",&n,&m)
#define scanddd(n,m,k) scanf("%d%d%d",&n,&m,&k)
#define scanlld(n) scanf("%lld",&n)
#define scanlldd(n,m) scanf("%lld%lld",&n,&m)
#define scanllddd(n,m,k) scanf("%lld%lld%lld",&n,&m,&k)
#define scans(str) scanf("%s",str)
#define ans() printf("%d",ans)
#define ansn() printf("%d\n",ans)
#define anss() printf("%d ",ans)
#define llans() printf("%lld",ans)
#define llanss() printf("%lld ",ans)
#define llansn() printf("%lld\n",ans)
#define REP(i,n) for(int i=0;i<(n);++i)
#define REA(i,qwe,ewr) for(int i=qwe;i<=ewr;++i)
#define RER(i,qwe,ewr) for(int i=qwe;i>=ewr;--i)
#define mst(abc,bca) memset(abc,bca,sizeof abc)
#define pii pair<int,int>
typedef long long ll;
const int mod = 1000000007;
const double eps=1e-9;
const int INF=0x3f3f3f3f;
const int MAXN=300005;
int a[MAXN];
vector<int>v[MAXN];
int ct1[MAXN],ct2[MAXN];
int main()
{
#ifdef LOCAL
    freopen("input.txt","r",stdin);
//    freopen("output.txt","w",stdout);
#endif // LOCAL

    int n;
    scand(n);
    int mx =-1e9;
    for(int i=1;i<=n;++i){
        scand(a[i]);
        mx = max(mx,a[i]);
    }
    for(int i=1;i<n;++i){
        int a,b;
        scandd(a,b);
        v[a].pb(b);
        v[b].pb(a);
    }
    int cnt1=0,cnt2=0,id=-1;
    int m1=0,m2=0;
    for(int i=1;i<=n;++i)
    {
        if(a[i]==mx)
        {
            ct1[i]++;
            ++cnt1;
            for(int j=0;j<v[i].size();++j)ct1[ v[i][j] ]++;
            id = i;
        }
        else if (a[i] == mx-1)
        {
            ct2[i]++;
            ++cnt2;
            for(int j=0;j<v[i].size();++j)ct2[ v[i][j] ]++;
        }
    }
    for(int i=1;i<=n;++i)
        m1 = max(m1,ct1[i]) , m2 = max(m2,ct2[i]);
    if(cnt1==1&&ct2[id]==cnt2)printf("%d",mx);
    else if(cnt1==m1)printf("%d",mx+1);
    else printf("%d",mx+2);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值