背景
\(Luogu \) \(P1122 / Codevs5565 \)
問題の意味
所与\(N- \)をノードに\(1 \)ツリーのルートノードとしてドット\(N-1 \)側と重量\((X_I、Y_I、W_i)\) 、枝の一部カットしようとしているの後に左\(Q \)最大側と右端を。
ソリューション
また、それは木である\(DP \)テンプレート。または演習。
ツリーはバイナリツリーですので、美しい自然があります:サブ木の枝の予約内の樹木は(これはあなたをでたらめされていない)サブツリーによって周りに取得することができます。
問題の意味を考慮すると、切断された分岐の一部、変換を左\(Qが\)すなわち左端\(Q + 1 \)ポイントを、ルートを含まなければなりません。
したがって、セットすることができる\(F_ {X、I} \) で表される(X \)\ノードのサブツリー根付い左(I \)\極大点とエッジの重み。
今回はそれが変換のトピックを検討し続けなければなりません。ルートは必須であるため、そう\(N-1 \)辺が残りに変換することができる\(N-1 \)の重みポイント(親の辺の重みに各点)。右側はそうと、最大の最大のポイントと右になりました。
幸い、その後転送される:レッツノード\(X \)左息子\(L_x \) 、右の子である(\ r_x)\次に、\(F_ {X、I} = \ MAX_ \制限{ Jは\ [0 ,. 1-I]は} \ {{L_x F_は、F_ {+} Jをr_x ,. 1-I-J} \} + val_x \)。\(l_x \)と\(r_x \)
発見のための方法は、下記を参照してください(トリック\)\それを。
騙す
所与\(N-1 \)条無向エッジ\(N(N \ leqslant 3 \回10 ^ 3)\) 左及び右の子ノードを発見するためのポイント二分木のルート方法:
隣接行列を確立し、これ保存する\(INT \)形式、\(G_ {X、Y} \)格納されている((X、Y)\ \ ) 右側の。
再帰的に上から下に、ルートからのビルドを開始しました。
各再帰へカレントノード\(Xは、\)ときに横断される\(N- \)ノードは、最初のノードとしての右側を見つけなければならない\(L_x \) 、次いで、再帰処理ノード。直接ループを出た後に返すことができます。探している(r_x \)\再反復するために注意\(N \)ノード、ノードとしてそれの最初の右側有する見つける(r_xを\)\次いで再帰ノードを処理します。
ディテール
2つの境界注:\(F_ {X、0} \)選択される基を表す(\ 0 \) 、答えが結合されているノード(\ 0)\ ; \ (L_x = 0 \)と\(r_x = 0 \)(その\(x \)サブツリーのリーフノードを選択することができない)、それ自体に直接戻り正しい値。
コード
\(ビュー\) \(コード\)
#include<bits/stdc++.h>
using namespace std;
inline int read()
{
int ret=0,f=1;
char ch=getchar();
while('9'<ch||ch<'0')
{
if(ch=='-')
f=-1;
ch=getchar();
}
while('0'<=ch&&ch<='9')
{
ret=(ret<<1)+(ret<<3)+ch-'0';
ch=getchar();
}
return ret*f;
}
int n,q,x,y,w,g[105][105],val[105],l[105],r[105],f[105][105];
void make_tree(int x)
{
for(register int i=1;i<=n;i++)
{
if(g[x][i]||!g[x][i])
{
val[i]=g[x][i];
g[x][i]=-1;
g[i][x]=-1;
l[x]=i;
make_tree(i);
break;
}
}
for(register int i=1;i<=n;i++)
{
if(g[x][i]||!g[x][i])
{
val[i]=g[x][i];
g[x][i]=-1;
g[i][x]=-1;
r[x]=i;
make_tree(i);
break;
}
}
}
int dp(int x,int cnt)
{
if(!cnt)
return 0;
if((!l[x])&&(!r[x]))
return val[x];
if(f[x][cnt])
return f[x][cnt];
for(register int i=0;i<cnt;i++)
{
f[x][cnt]=max(f[x][cnt],dp(l[x],i)+dp(r[x],cnt-1-i)+val[x]);
}
return f[x][cnt];
}
int main()
{
n=read();
q=read();
memset(g,-1,sizeof(g));
for(register int i=1;i<n;i++)
{
x=read();
y=read();
w=read();
g[x][y]=w;
g[y][x]=w;
}
make_tree(1);
printf("%d\n",dp(1,q+1));
return 0;
}