题解 树上路径

传送门

因为正解就是随机化+数据水了,所以暴力可以 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;
}
posted @ 2022-04-08 21:18  Administrator-09  阅读(1)  评论(0编辑  收藏  举报