Codeforces 796C Bank Hacking 貪心+規律
阿新 • • 發佈:2019-02-11
題意: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; }