題解 P3647 [APIO2014]連珠線
題解
這裡提供一種與其他題解不太相同的做法。
首先最後的樹中只可能有這兩種藍線。
其次如果把一個為藍線端點的點當作根,那就只有第一種情況。
因此一個做法是,列舉每個點為根時,根據第一種情況進行樹形 dp。
具體的,設 \(w_{i,j}\) 表示 \(i,j\) 之間連線的權值,\(f_{i,0/1}\) 表示 \(1\) 為根時 \(i\) 的子樹中,\(i\) 是/不是藍線中點時答案的最大值。
\(f_{i,0}=\underset{v\in son_i}{\sum}\max(f_{v,0},f_{v,1}+w_{i,v})\)
\(f_{i,1}=f_{i,0}+\underset{v\in son_i}{\max}(f_{v,1}+w_{i,v}-f_{v,0})\)
接下來進行換根操作。
具體的,設 \(j\) 為 \(i\) 的某個兒子,\(dp_{i,0/1,j}\) 表示 \(1\) 為根時 \(i\) 的子樹中,不考慮 \(j\) 的子樹,\(i\) 是/不是藍線中點時答案的最大值,\(g_{i,0/1}\) 表示 \(1\) 為根時整棵樹中,不考慮 \(i\) 的子樹,\(i\) 是/不是藍線中點時答案的最大值。
答案即為 \(f_{i,0}+g_{i,0}\) 的最大值。
\(dp_{i,0,j}=\underset{v\in son_i,v\neq j}{\sum}\max(f_{v,0},f_{v,1}+w_{i,v})\)
\(dp_{i,1,j}=dp_{i,0,j}+\underset{v\in son_i,v\neq j}{\max}(f_{v,1}+w_{i,v}-f_{v,0})\)
\(g_{j,0}=\max(g_{i,0}+dp_{i,0,j},\max(g_{i,1}+dp_{i,0,j},g_{i,0}+dp_{i,1,j})+w_{i,j}\)
\(g_{j,1}=g_{i,0}+dp_{i,0,j}+w_{i,j}\)
具體實現用 vector 和最大次大值維護一下即可 \(O(1)\) 換根。
Code
#include<cstdio> #include<vector> #define inf 0x3f3f3f3f using namespace std; int n,edge_t=0,ans=0; int la[200002]={},mx[200002],sec[200002]; int f[200002][2],g[200002][2]; vector<int> dp[200002][2]; struct aaa { int to,nx,val; }edge[400002]; inline int max(int x,int y) { return x>y? x:y; } inline void add_edge(int x,int y,int z) { edge[++edge_t]=(aaa){y,la[x],z},la[x]=edge_t; edge[++edge_t]=(aaa){x,la[y],z},la[y]=edge_t; } inline void dfs(int x,int fa) { mx[x]=sec[x]=-inf,f[x][0]=0; for(int i=la[x],v,w,w1;i;i=edge[i].nx) if((v=edge[i].to)!=fa) { dp[x][0].push_back(0),dp[x][1].push_back(0),dfs(v,x),f[x][0]+=(w1=max(f[v][0],f[v][1]+edge[i].val)); if((w=f[v][0]+edge[i].val-w1)>mx[x])sec[x]=mx[x],mx[x]=w;else if(w>sec[x])sec[x]=w; } f[x][1]=f[x][0]+mx[x]; } inline void dfs1(int x,int fa) { int t=0; for(int i=la[x],v;i;i=edge[i].nx) if((v=edge[i].to)!=fa) { dp[x][0][t]=dp[x][1][t]=f[x][0]-max(f[v][0],f[v][1]+edge[i].val),dfs1(v,x); if(mx[x]==f[v][0]+edge[i].val-max(f[v][0],f[v][1]+edge[i].val))dp[x][1][t]+=sec[x];else dp[x][1][t]+=mx[x]; ++t; } } inline void dfs2(int x,int fa) { int t=0;ans=max(ans,f[x][0]+g[x][0]); for(int i=la[x],v;i;i=edge[i].nx) if((v=edge[i].to)!=fa) g[v][0]=max(g[x][0]+dp[x][0][t],max(g[x][1]+dp[x][0][t],g[x][0]+dp[x][1][t])+edge[i].val),g[v][1]=g[x][0]+dp[x][0][t]+edge[i].val,dfs2(v,x),++t; } int main() { g[1][0]=0,g[1][1]=-inf,scanf("%d",&n); for(int i=1,x,y,z;i<n;++i)scanf("%d%d%d",&x,&y,&z),add_edge(x,y,z); dfs(1,0),dfs1(1,0),dfs2(1,0),printf("%d",ans); return 0; }