1. 程式人生 > >2018.10.25【NOIP練習】最大瘋子樹(樹形DP)

2018.10.25【NOIP練習】最大瘋子樹(樹形DP)

傳送門

解析:

其實簡單推一下我們發現一個瘋子樹內部任何一條路徑上點權都是單峰下凸的。

證明也很簡單,不過請記住一點,考場上沒有必要去想證明,除非你時間真的很充裕。

必要性:如果不是單峰下凸,則不是瘋子樹。
考察如果尋在一條不是單峰下凸的路徑,那麼這個路徑的某一個端點與其±1\pm 1的端點的路徑必然會有一條經過這條路徑上的峰值的位置,顯然就不滿足瘋子樹的性質。

充分性:如果所有路徑都是單峰下凸,則必然是瘋子樹。
這個就不證了,十分顯然。

有了上面的結論後我們知道,每個瘋子樹內部必然有一個點權最小的節點,我們就在這個點權最小的節點處統計答案。

注意一下點權相等的節點,如果他們在同一棵瘋子樹內部,則他們自己必然形成一個聯通子樹。我們只需要在最高的節點處統計答案就行了。

統計還是分成從兒子轉移和從父親轉移就行了。

程式碼:

#include<bits/stdc++.h>
using namespace std;
#define ll long long 
#define re register
#define gc getchar
#define pc putchar
#define cs const

inline int getint(){
	re int num;
	re char c;
	while(!isdigit(c=gc()));num=c^48;
	while(isdigit(c=gc()))num=(num<<1)+
(num<<3)+(c^48); return num; } inline void outint(int a){ static char ch[13]; if(a==0)pc('0'); while(a)ch[++ch[0]]=a-a/10*10,a/=10; while(ch[0])pc(ch[ch[0]--]^48); } cs int N=200005; int last[N],nxt[N<<1],to[N<<1],ecnt; inline void addedge(int u,int v){ nxt[++ecnt]=last[u],last[u]=ecnt,to[
ecnt]=v; nxt[++ecnt]=last[v],last[v]=ecnt,to[ecnt]=u; } int val[N],fa[N]; int g[N],f[N]; inline void dfs1(int u){ g[u]=1; for(int re e=last[u],v=to[e];e;v=to[e=nxt[e]]){ if(v==fa[u])continue; fa[v]=u; dfs1(v); if(val[v]>=val[u])g[u]+=g[v]; } } int ans=0; inline void dfs2(int u){ ans=max(ans,g[u]+f[u]); for(int re e=last[u],v=to[e];e;v=to[e=nxt[e]]){ if(v==fa[u])continue; if(val[u]>val[v])f[v]=g[u]+f[u]; else f[v]=0; dfs2(v); } } int n; signed main(){ while(~scanf("%d",&n)){ ecnt=0; memset(last,0,sizeof last); for(int re i=1;i<=n;++i)val[i]=getint(); for(int re i=1;i<n;++i){ int u=getint(),v=getint(); addedge(u,v); } ans=0; dfs1(1); dfs2(1); outint(ans);pc('\n'); } return 0; }