「AT2210 木の問題 / Problem on Tree」
阿新 • • 發佈:2020-11-29
題目大意
在樹上按順序選出一些點,使得相鄰的兩點的樹上路徑中不存在其他被選的點,求可以選點的最大個數.
分析
這裡給出一種比較麻煩的非常暴力的做法.
考慮直接樹形 \(\operatorname{dp}\).首先可以發現對於一棵樹這棵樹的所有度為 \(1\) 的點必然是可以同時選擇的(這裡度指無根樹中一個點所連線的點的個數).有根樹比較難處理,下面假定 \(1\) 為根.那麼如果需要選擇一棵子樹中的點且選擇的這些點並不是在整個選擇點的開頭位置或者結尾位置,這顆子樹中第一個選擇的點前面存在點,最後一個選擇的點後面也有點,那麼這顆子樹必然是選擇葉節點最優(證明略,可以自行畫圖理解)現在設 \(f_i\)
可以得到轉移方程:
\[f_i=\max\{g_i,f_j+1,f_j+g_i-g_j\} \](其中 \(j\) 為 \(i\) 的子節點,\(f_j+1\) 表示選擇一個節點往下並且不上來,那麼顯然 \(i\) 這個點也可以選擇,\(f_j+g_i-g_j\) 表示選擇一個點向下,並且走完 \(i\) 的其他子樹的葉節點)
那麼答案也是分成兩種情況,一種是起點為根節點,也就是 \(f_i\)
(其中 \(j,k\) 為 \(i\) 的兩個不同的子節點)
這種情況可以選擇每個節點的子節點(\(j\))中 \(f_j\) 的最大值和次大值得出.
另一種情況為不選擇這個但是選擇這個點所連出去的度為 \(1\) 的節點,也就是:
\[\max\{f_j+f_k+g_1-g_i-g_j\} \]這種情況可以選擇每個幾點的子節點(\(j\)
這樣這題就做完了.
程式碼
#include<bits/stdc++.h>
#define REP(i,first,last) for(int i=first;i<=last;++i)
#define DOW(i,first,last) for(int i=first;last<=i;--i)
namespace IO
//快讀模板
using namespace IO;
using namespace std;
const int MAXN=1e6+5;
template<int MAXN,int MAXM>class Edge
{
//存圖模板
#define FOR(now_edge,now) //遍歷 now 的出邊
#define TO(now_edge) //出邊
};
Edge<MAXN,MAXN<<1>e;
int n;
int f[MAXN],g[MAXN];
bool le[MAXN];//判斷一個節點是否為葉節點
int answer=0;
void DFS(int now=1,int father=0)//計算 f,g
{
bool flag=1;
FOR(e,now)
{
if(TO(e)^father)
{
flag=0;
DFS(TO(e),now);
g[now]+=g[TO(e)];
}
}
le[now]+=flag;
g[now]+=flag;
f[now]=g[now];
FOR(e,now)
{
if(TO(e)^father)
{
f[now]=Max(f[now],f[TO(e)]+g[now]-g[TO(e)],f[TO(e)]+1);//轉移
}
}
}
void DFS_2(int now=1,int father=0)//計算每個節點為 LCA 時的答案
{
int max1=0,max2=0;//f[j] 的最大值和次大值
int max3=0,max4=0;//f[j]-g[j] 的最大值和次大值
FOR(e,now)
{
if(TO(e)^father)
{
DFS_2(TO(e),now);
if(max1<f[TO(e)])
{
max2=max1;
max1=f[TO(e)];
}
else
{
if(max2<f[TO(e)])
{
max2=f[TO(e)];
}
}
if(max3<f[TO(e)]-g[TO(e)])
{
max4=max3;
max3=f[TO(e)]-g[TO(e)];
}
else
{
if(max4<f[TO(e)]-g[TO(e)])
{
max4=f[TO(e)]-g[TO(e)];
}
}
}
}
answer=Max(answer,max1+max2+1,g[1]+max3+max4);//統計兩種情況
}
int main()
{
Read(n);
int u,v,tot=0;
REP(i,1,n-1)
{
Read(u,v);
tot+=u==1||v==1;
e[u]+=e[v]+=u;
}
if(tot==1)//特判根節點為葉節點的情況
{
le[1]=1;
g[1]=1;
}
DFS();
DFS_2();
Writeln(Max(answer,f[1]));//輸出答案
return 0;
}