bzoj4543 [POI2014]Hotel加强版 长链剖分+树形DP

题目传送门

https://lydsy.com/JudgeOnline/problem.php?id=4543

题解

这道题的弱化版 bzoj3522 [POI2014]Hotel 的做法有好几种吧。

我一开始是另一种做法,所以别人说这个题目可以有长链剖分来加速的时候怎么也想不出来。

枚举 \(i\),令点 \(i\) 为根,统计三个人的中心是 \(i\) 的情况。首先三个人一定在不同的子树中,然后分层统计一下就好了。


还有一个种纯 dp 的做法:

\(dp[x][i]\) 表示 \(x\) 的子树中有多少距离 \(x\)\(i\) 的点,\(f[x][i]\) 的子树中有多少点对的 \(lca\)(两个点到 \(lca\) 的距离均为 \(d\))距离 \(x\)\(d-i\)

这样,合并两个子树的时候考虑先用两个子树 \(dp\) 贡献出 \(f\),然后再各自对各自贡献相加即可。


但是这样做的时间复杂度 \(O(n^2)\)

因为在这个做法中,第二维只和深度有关,所以考虑长链剖分,把轻儿子合并到重儿子上,因为这样做实际上于长度的值上,每一条长链只会被计算一次,所以时间复杂度为 \(O(n)\)

具体的实现上可以使用指针移位来实现。


#include<bits/stdc++.h>

#define fec(i, x, y) (int i = head[x], y = g[i].to; i; i = g[i].ne, y = g[i].to)
#define dbg(...) fprintf(stderr, __VA_ARGS__)
#define File(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define fi first
#define se second
#define pb push_back

template<typename A, typename B> inline char smax(A &a, const B &b) {return a < b ? a = b, 1 : 0;}
template<typename A, typename B> inline char smin(A &a, const B &b) {return b < a ? a = b, 1 : 0;}

typedef long long ll; typedef unsigned long long ull; typedef std::pair<int, int> pii;

template<typename I> inline void read(I &x) {
	int dp = 0, c;
	while (!isdigit(c = getchar())) c == '-' ? dp = 1 : 0;
	x = c & 15;
	while (isdigit(c = getchar())) x = (x << 1) + (x << 3) + (c & 15);
	dp ? x = -x : 0;
}

const int N = 1e5 + 7;

int n;
ll ans;
int len[N], son[N];
ll a[N << 2], *dp[N], *f[N], *now1, *now2;

struct Edge { int to, ne; } g[N << 1]; int head[N], tot;
inline void addedge(int x, int y) { g[++tot].to = y, g[tot].ne = head[x], head[x] = tot; }
inline void adde(int x, int y) { addedge(x, y), addedge(y, x); }

inline void dfs1(int x, int fa = 0) {
	len[x] = 1;
	for fec(i, x, y) if (y != fa) dfs1(y, x), smax(len[x], len[y] + 1) && (son[x] = y);
}

inline void init(int x) {
	dp[x] = now1, now1 += len[x];
	now2 += len[x], f[x] = now2, now2 += len[x];
	assert(now1 - a <= n), assert(now2 - a <= n * 3);
}

inline void dfs2(int x, int fa = 0) {
	if (son[fa] != x) init(x);
	if (son[x]) dp[son[x]] = dp[x] + 1, f[son[x]] = f[x] - 1, dfs2(son[x], x);
	dp[x][0] = 1, ans += f[x][0];
	for fec(i, x, y) if (y != fa && y != son[x]) {
		dfs2(y, x);
		for (int i = 0; i < len[y]; ++i) i && (ans += dp[x][i - 1] * f[y][i]), i + 1 < len[x] && (ans += f[x][i + 1] * dp[y][i]);
		for (int i = 0; i < len[y]; ++i) {
			if (i + 1 < len[x]) f[x][i + 1] += dp[x][i + 1] * dp[y][i], dp[x][i + 1] += dp[y][i];
			if (i) f[x][i - 1] += f[y][i];
		}
	}
}

inline void work() {
	dfs1(1);
	now1 = a, now2 = a + n;
	dfs2(1);
	printf("%lld\n", ans);
}

inline void init() {
	read(n);
	int x, y;
	for (int i = 1; i < n; ++i) read(x), read(y), adde(x, y);
}

int main() {
#ifdef hzhkk
	freopen("hkk.in", "r", stdin);
#endif
	init();
	work();
	fclose(stdin), fclose(stdout);
	return 0;
}
posted @ 2019-10-14 14:33  hankeke303  阅读(112)  评论(0编辑  收藏  举报