题意:
桌子上有 \(1\) 个珠子,你要进行 \(n-1\) 次操作,每次操作有以下两种类型:
显然最后 \(n\) 个珠子会形成一棵由 \(n-1\) 条线的树,给出最终每条线的长度,但你不知道它们的颜色。
你的得分为所有蓝线长度之和,求在所有可能的情况下,你得分的最大值。
\(n \in [2,2\times 10^5]\)
考虑蓝线的性质。由于连成的蓝线就没办法再被断开了,故蓝线连接的三个节点 \(u,w,v\) 在最终的树上也是相邻的。
故原题可以转化为:你可以选择一条三个节点 \(u\to v\to w\) 的链满足边 \((u,v),(v,w)\) 都没被选择,要求选出的边的权值之和的最大值。
树上相邻的三个节点有两种可能,一是爷爷->父亲->儿子,二是儿子->父亲->兄弟。
第二种情况比较复杂。不过我们可以枚举最开始的珠子在最终的树上的编号是多少,也就是钦定一个根节点,这样就不会出现第二种情况(很好理解,如果出现儿子->父亲->兄弟的情况,那我们肯定是先连好儿子->兄弟的边,再插入父亲节点,而由于父亲节点与根节点相连,所以应当是父亲节点先连好才对,所以这种情况不可能出现)
然后就可以 \(dp\) 了。\(dp_{u,0}\) 选好了 \(u\) 的子树中的边,\(u\) 不是某条蓝链的中点的最大权值和。\(dp_{u,1}\) 表示 \(u\) 是某条蓝链的中点的最大权值和。
考虑转移,\(dp_{u,0}\) 比较简单,枚举它的每个儿子 \(v\),有两种情况,要么 \((u,v)\) 间连了条红线,也就是 \(dp_{v,0}\),要么 \((u,v)\) 间连了条蓝线,而由于 \(u\) 不是某条蓝链的中点,所以这条链的连法只可能是 \(u\to v\to v\) 的某个儿子,也就是 \(dp_{v,1}+w\),故 \(dp_{u,0}=\sum\limits_{v\in son_u}\max(dp_{v,0},dp_{v,1}+w)\)
再考虑 \(dp_{u,1}\),显然 \(dp_{u,1}\) 是在 \(dp_{u,1}\) 是在 \(dp_{u,0}\) 的基础上将 \(u\) 与某个儿子 \(v\) 节点之间的边换成蓝边,考虑这一类树形 \(dp\) 的常用套路,枚举这个儿子 \(v\),计算将 \((u,v)\) 之间的边改为蓝边造成的 \(\Delta=dp_{v,0}+w-\max(dp_{v,0},dp_{v,1}+w)\),然后取个 \(\max\) 即可,故 \(dp_{u,1}=dp_{u,0}+\max\limits_{v\in son_u}dp_{v,0}+w-\max(dp_{v,0},dp_{v,1}+w)\)
然后考虑换根,记 \(f_{u}\) 表示以 \(u\) 为根节点的答案,\(out_{i,0/1}\) 表示 \(i\) 子树外的 \(dp\) 值,加法是可逆的,至于 \(\max\), multiset 维护一下即可。这一部分比较套路,具体见代码。
时间复杂度 \(\mathcal O(n\log n)\)
#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fz(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define ffe(it,v) for(__typeof(v.begin()) it=v.begin();it!=v.end();it++)
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,63,sizeof(a))
#define pb push_back
#define ppb pop_back
#define mp make_pair
typedef pair<int,int> pii;
typedef long long ll;
const int MAXN=2e5+5;
int n,to[MAXN<<1],nxt[MAXN<<1],hd[MAXN],cst[MAXN<<1],ec=0;
void adde(int u,int v,int w){
to[++ec]=v;nxt[ec]=hd[u];cst[ec]=w;hd[u]=ec;
}
int dp[MAXN][2],f[MAXN],out[MAXN][2];
void dfs(int x,int fa){
dp[x][1]=-2e9;
for(int e=hd[x];e;e=nxt[e]){
int y=to[e],z=cst[e];if(y==fa) continue;dfs(y,x);
dp[x][0]+=max(dp[y][0],dp[y][1]+z);
dp[x][1]=max(dp[x][1],dp[y][0]+z-max(dp[y][0],dp[y][1]+z));
} dp[x][1]+=dp[x][0];
}
void cgrt(int x,int fa){
int sum=0;
multiset<int> st;st.insert(-2e9);
for(int e=hd[x];e;e=nxt[e]){
int y=to[e],z=cst[e];
if(y==fa){
sum+=max(out[x][0],out[x][1]+z);
st.insert(out[x][0]+z-max(out[x][0],out[x][1]+z));
} else {
sum+=max(dp[y][0],dp[y][1]+z);
st.insert(dp[y][0]+z-max(dp[y][0],dp[y][1]+z));
}
}
for(int e=hd[x];e;e=nxt[e]){
int y=to[e],z=cst[e];if(y==fa) continue;
out[y][0]=sum-max(dp[y][0],dp[y][1]+z);
st.erase(st.find(dp[y][0]+z-max(dp[y][0],dp[y][1]+z)));
out[y][1]=out[y][0]+(*st.rbegin());
st.insert(dp[y][0]+z-max(dp[y][0],dp[y][1]+z));
f[y]=dp[y][0]+max(out[y][0],out[y][1]+z);
cgrt(y,x);
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<n;i++){
int u,v,w;scanf("%d%d%d",&u,&v,&w);
adde(u,v,w);adde(v,u,w);
} dfs(1,0);f[1]=dp[1][0];cgrt(1,0);int ans=0;
for(int i=1;i<=n;i++) ans=max(ans,f[i]);
printf("%d\n",ans);
return 0;
}
手机扫一扫
移动阅读更方便
你可能感兴趣的文章