题解 树上路径
因为正解就是随机化+数据水了,所以暴力可以 AC
然而我不会打暴力,于是……
- 一个 \(\pm1\) 序列前缀和的最大值是低于 \(\sqrt n\) 级别的
首先容易想到一个状态 \(f_{i, j}\) 为 \(i\) 子树中向上延伸的链长为 \(j\) 的最大权
考虑怎么合并子树
赛时:维护偏移量?跑网络流?
赛后:md 这不随便 DP 一下就行了
好像忘了合并子树信息的时候是可以再做一个 DP 的
以 \(k=3\) 为例,令 \(g_{i, j}\) 为 \(i\) 的直系儿子中长为 1 的比长为 3 的多 \(j\) 个的最大权
每次加入一个儿子即可
根据上面的结论第二维只需要开到根号(注意需要将边集随机打乱才可以)
\(k=4\) 是类似的,加一维记录长为 2 的边数的奇偶性即可
复杂度 \(O(n\sqrt n)\)
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f3f3f3f3f
#define N 200010
#define fir first
#define sec second
#define pb push_back
#define ll long long
//#define int long long
char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int n, k;
vector<pair<int, int>> e[N];
inline void chkmax(ll& a, ll b) {a=max(a, b);}
namespace task1{
int now;
ll f[N][4], tg[2][N], top[N], *g[2];
void dfs(int u, int fa) {
// cout<<"dfs: "<<u<<' '<<fa<<endl;
for (auto v:e[u])
if (v.fir!=fa) top[v.fir]=v.sec, dfs(v.fir, u);
int lim=min((int)e[u].size(), 500);
for (int i=-lim; i<=lim; ++i) g[now][i]=-INF;
g[now][0]=0;
for (auto v:e[u]) if (v.fir!=fa) {
for (int i=-lim; i<=lim; ++i) g[now^1][i]=-INF;
for (int i=-lim; i<=lim; ++i) {
chkmax(g[now^1][i+1], g[now][i]+f[v.fir][1]);
chkmax(g[now^1][i-1], g[now][i]+f[v.fir][2]);
chkmax(g[now^1][i], g[now][i]+max(f[v.fir][0], f[v.fir][3]));
}
now^=1;
}
f[u][0]=g[now][0];
f[u][1]=g[now][0]+top[u];
f[u][2]=g[now][1]+top[u];
f[u][3]=g[now][-1]+top[u];
// cout<<"f: "<<f[u][0]<<endl;
// cout<<"g: "; for (int i=-10; i<=10; ++i) cout<<g[now][i]<<' '; cout<<endl;
}
void solve() {
g[0]=tg[0]+100000; g[1]=tg[1]+100000;
dfs(1, 0);
cout<<f[1][0]<<endl;
}
}
namespace task2{
int now;
ll f[N][45], tg[2][N][2], top[N];
ll (*g[2])[2];
void dfs(int u, int fa) {
// cout<<"dfs: "<<u<<' '<<fa<<endl;
for (auto v:e[u])
if (v.fir!=fa) top[v.fir]=v.sec, dfs(v.fir, u);
int lim=min((int)e[u].size(), 500);
for (int i=-lim; i<=lim; ++i) g[now][i][0]=g[now][i][1]=-INF;
g[now][0][0]=0;
for (auto v:e[u]) if (v.fir!=fa) {
for (int i=-lim; i<=lim; ++i) g[now^1][i][0]=g[now^1][i][1]=-INF;
for (int i=-lim; i<=lim; ++i) {
for (int j=0; j<2; ++j) {
chkmax(g[now^1][i+1][j], g[now][i][j]+f[v.fir][1]);
chkmax(g[now^1][i-1][j], g[now][i][j]+f[v.fir][3]);
chkmax(g[now^1][i][j^1], g[now][i][j]+f[v.fir][2]);
chkmax(g[now^1][i][j], g[now][i][j]+max(f[v.fir][0], f[v.fir][4]));
}
}
now^=1;
}
f[u][0]=g[now][0][0];
f[u][1]=g[now][0][0]+top[u];
f[u][2]=g[now][1][0]+top[u];
f[u][3]=g[now][0][1]+top[u];
f[u][4]=g[now][-1][0]+top[u];
// cout<<"f: "<<f[u][0]<<endl;
// cout<<"g: "; for (int i=-10; i<=10; ++i) cout<<g[now][i]<<' '; cout<<endl;
}
void solve() {
g[0]=tg[0]+100000; g[1]=tg[1]+100000;
dfs(1, 0);
cout<<f[1][0]<<endl;
}
}
signed main()
{
freopen("c.in", "r", stdin);
freopen("c.out", "w", stdout);
random_device seed;
mt19937 rand(seed());
n=read(); k=read();
for (int i=1,u,v,w; i<n; ++i) {
u=read(); v=read(); w=read();
e[u].pb({v, w}); e[v].pb({u, w});
}
for (int i=1; i<=n; ++i) shuffle(e[i].begin(), e[i].end(), rand);
if (k==3) task1::solve();
else task2::solve();
return 0;
}