POJ 1741 树形dp

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int Maxn = 2E5 + 10;
const int Maxm = 1E5 + 10;
const int INF = 0x3f3f3f3f;
int N, K, first[Maxm], x, y, z, Next[Maxm], v[Maxm], w[Maxm], cnt, res, q[Maxn], fa[Maxn], p[Maxn], del[Maxn], size[Maxn];
void add(int x, int y, int z)
{
	v[cnt] = y, w[cnt] = z, Next[cnt] = first[x], first[x] = cnt++;
}
int findroot(int cur)
{
	int i, j, x, rear = 0, Max, min = INF, root;
	q[rear ++] = cur, fa[cur] = 0;
	for (i = 0; i < rear; i ++)
	{
		x = q[i];
		for (j = first[x]; j != -1; j = Next[j])
			if (!del[v[j]] && v[j] != fa[x])
				q[rear ++] = v[j], fa[v[j]] = x;
	}
	for (i = rear - 1; i >= 0; i --)
	{
		x = q[i];
		size[x] = 1, Max = 0;
		for (j = first[x]; j != -1; j = Next[j])
			if (!del[v[j]] && v[j] != fa[x])
				size[x] += size[v[j]], Max = max(Max, size[v[j]]);
		Max = max(Max, rear - size[x]);
		if (Max < min)
			min = Max, root = x;
	}
	return root;
}
int deal(int s, int t)
{
	int i, rear = t, ans = 0;
	for (i = s; i <= t; i ++)
	{
		while (rear >= s && p[rear] + p[i] > K)
			-- rear;
		ans += rear - s + 1;
	}
	return ans;
}
void renew(int s, int cur, int d)
{
	int i, j, x, rear = 0;
	q[rear ++] = cur, fa[cur] = 0, p[s] = d;
	for (i = 0; i < rear; i ++)
	{
		x = q[i];
		for (j = first[x]; j != -1; j = Next[j])
			if (!del[v[j]] && v[j] != fa[x])
				p[s + rear] = p[s + i] + w[j], q[rear ++] = v[j], fa[v[j]] = x;
	}
	sort(p + s, p + s + rear);
}
int dfs(int s, int cur, int d)
{
	int i, root, n, tot = 1;
	root = findroot(cur);
	del[root] = 1;
	for (i = first[root]; i != -1; i = Next[i])
		if (!del[v[i]])
		{
			n = dfs(s + tot, v[i], w[i]);
			res -= deal(s + tot, s + tot + n - 1);
			tot += n;
		}
	p[s] = 0;
	sort(p + s, p + s + tot);
	res += deal(s, s + tot - 1) - 1;
	del[root] = 0;
	renew(s, cur, d);
	return tot;
}
int main(int argc, char const *argv[])
{
	while (~scanf("%d%d", &N, &K) && N + K)
	{
		cnt = 0; res = 0;
		memset(first, -1, sizeof(first));
		for (int i = 1; i < N; i++)
			scanf("%d%d%d", &x, &y, &z), add(x, y, z), add(y, x, z);
		dfs(0, 1, 0);
		printf("%d\n", res >> 1);
	}
	return 0;
}


 恩,楼教主的八题之一,需要优化的树形dp,具体思路有漆子超的《分治算法在树的路径问题中的应用》这篇论文。

题解思路来自:http://blog.sina.com.cn/s/blog_6d5aa19a0100o73m.html

//__________________________________________________________

对于一棵有根树, 树中满足要求的一个数对所对应的一条路径,必然是以下两种情况之一:
1、经过根节点
2、不经过根节点,也就是说在根节点的一棵子树中
对于情况2,可以递归求解,下面主要来考虑情况1。

设X为满足i<j且Depth[i]+Depth[j]<=K的数对(i,j)的个数
设Y为满足i<j,Depth[i]+Depth[j]<=K且Belong[i]=Belong[j]数对(i,j)的个数
那么我们要统计的量便等于X-Y


求X、Y的过程均可以转化为以下问题:
已知A[1],A[2],...A[m],求满足i<j且A[i]+A[j]<=K的数对(i,j)的个数


对于这个问题,我们先将A从小到大排序。
设B[i]表示满足A[i]+A[p]<=K的最大的p(若不存在则为0)。我们的任务便转化为求出A所对应的B数组。那么,若B[i]>i,那么i对答案的贡献为B[i]-i。
显然,随着i的增大,B[i]的值是不会增大的。利用这个性质,我们可以在线性的时间内求出B数组,从而得到答案。


综上,设递归最大层数为L,因为每一层的时间复杂度均为“瓶颈”——排序的时间复杂度O(NlogN),所以总的时间复杂度为O(L*NlogN)

//_________________________________________________________________________________________________________



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值