Codeforces 796C Bank Hacking 贪心+规律

点击打开链接

题意:n个结点,n-1条边,每个点价值为a[i],两点有边直接相连,算相邻,i,j半相邻:存在没被攻击的中间点k,(i,k),(j,k)是相邻的 
攻击i后,和i相邻和半相邻的点a[k]++,n<=3e5,求攻击n个点需要的最小x?
除了第一次外,每次攻击的点必须满足:1:online,2:和某个offline相邻,3:a[i]<=x


n点,n-1条边且连通,则为无根的树,任取一点为根
关键在于条件2:每次能被攻击的点都要和offline相连->任意一点u的值最多+2 
通过第一次操作后.对任意点u
若当u的祖先被攻击时,u最多+2,此时它的任意子结点v,只有当u被攻击时才能被攻击,子节点对u贡献为0
若为u的某个子树被攻击,u的其余子树和u的祖先 都只有在u被攻击时才被攻击,所以任意点u的值最多+2,ans<=mx+2 


综上:第一次攻击的点+0,其相邻点+1 其余点都+2,
C1为mx个数  C2为mx-1个数 
ans=mx 只有当C1=1&&正好有C2个mx-1与mx相连  
ans=mx+1 只有当存在一个点满足 其距离<=1内 mx的个数为C1 
其余情况ans=mx+2

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll,ll> ii;
const ll inf=1e10;
const int N=2e6+20;
ll n,a[N],vis[N],can[N]; 
vector<int> e[N];
void solve()
{
	ll C1=0,C2=0,mx=-inf,u;
	for(int i=1;i<=n;i++)
		mx=max(a[i],mx);
	for(int i=1;i<=n;i++)
	{
		if(a[i]==mx)
			C1++,u=i;
		else if(a[i]==mx-1)
			C2++; 
	}
	ll ans=inf;
	if(C1==1)//
	{
		int cnt=0;
		for(int i=0;i<e[u].size();i++)
		{
			int v=e[u][i];
			if(a[v]==mx-1)
				cnt++;
		}
		if(cnt==C2)
			ans=mx;
	}
	if(ans==inf)
	{
		for(int i=1;i<=n&&ans==inf;i++)//遍历边O(n),找到相邻为1内,有C1个mx  
		{
			ll res=0;
			if(a[i]==mx)
				res++;
			for(int j=0;j<e[i].size();j++)
			{
				int v=e[i][j];
				if(a[v]==mx)
					res++;
			}
			if(res==C1)
				ans=mx+1;
		}
	}
	if(ans==inf)
		ans=mx+2;
	cout<<ans<<endl;
}
int main()
{
	while(cin>>n)
	{
		for(int i=1;i<=n;i++)
			scanf("%I64d",&a[i]),e[i].clear();
		int u,v;
		for(int i=1;i<=n-1;i++)
		{
			scanf("%d%d",&u,&v);
			e[u].push_back(v);
			e[v].push_back(u);
		}
		solve();
	}
	return 0;
}

 

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值