1. 程式人生 > >Codeforces 796C Bank Hacking 貪心+規律

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;
}