CF796C Bank Hacking
题目链接
分析
题目好长
其实也不是很长,我感觉按照原题的说法去理解这个题会比较好理解一些。
就是说,我先从树上找到一个点,然后一步一步的去覆盖这棵树,你只能覆盖点权比你小的点,问覆盖整棵树需要你的权值最小是多少。
首先先考虑我会去从哪个点开始,显然是先覆盖点权大的,因为它迟早都要被覆盖,如果现在不去覆盖,那再覆盖它,它的点权就会增加,所以肯定要先覆盖点权大的,进而可以知道,一个点如果有可能成为最大值,它的点权最多只会被+2,因为我要去覆盖它,首先覆盖它的半相邻点,再去覆盖它的相邻点,
如果它变大了,那我下一步就要把它覆盖掉,因此答案最多只可能是最大值+2。
接下来就是一个分类讨论了,假设最大值为\(max\),我们只要找\(max-1\)的点就行了,因为\(max-2\)的点最多也只会被搞成\(max\),如果所有的\(max-1\)的点都连在\(max\)上,那我搞完\(max\)就能在他们增加1到\(max-1+1\)的时候,破坏掉他们,所以此时答案为\(max\),如果一个\(max-1\)的点也没有,那答案显然也是这个。
如果他们不连在一起呢?
那我覆盖\(max\)和6的时候,\(max-1\)都要+1,最后就成了\(max+1\),所以当没有连在一起的时候,就是\(max+1\)。
那么有不止一个\(max\)的时候呢?
如果这些都连在一起,答案是\(max+1\),不管是连在\(max\)上还是别的上边,只要他们互为相邻点或半相邻点就好。
那么跟上边推\(max-1\)的时候一样,只不过\(max\)最多变成\(max+2\)。
也就这几种情况了,模拟一下就出来??
#include<cstdio>
#include<limits>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=3e5+10;
struct Edge{
int to,nxt;
}e[N<<1];
int h[N],idx,w[N];
void Ins(int a,int b){
e[idx].to=b;e[idx].nxt=h[a];h[a]=idx++;
}
int main(){
memset(h,-1,sizeof(h));
int n,mx,cnt,cntsec,pos;
mx=INT_MIN;cntsec=cnt=0;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&w[i]);
if(w[i]>mx){
mx=w[i];
pos=i;
}
}
for(int i=1;i<=n;i++){
if(w[i]==mx)cnt++;
if(w[i]==mx-1)cntsec++;
}
for(int i=1;i<n;i++){
int a,b;
scanf("%d%d",&a,&b);
Ins(a,b);
Ins(b,a);
}
if(cnt==1){
if(cntsec==0){
printf("%d\n",mx);
return 0;
}
int nxt=0;
for(int i=h[pos];~i;i=e[i].nxt){
if(w[e[i].to]==mx-1)nxt++;
}
if(nxt==cntsec)
printf("%d\n",mx);
else printf("%d\n",mx+1);
return 0;
}
if(cnt>1){
for(int i=1;i<=n;i++){
int nxt=0;
for(int j=h[i];~j;j=e[j].nxt){
if(w[e[j].to]==mx)nxt++;
}
if((nxt==cnt&&w[i]!=mx)||(nxt==cnt-1&&w[i]==mx)){
printf("%d\n",mx+1);
return 0;
}
}
}
printf("%d\n",mx+2);
return 0;
}
int - > long long
0 - > 100