spoj FTOUR2(树的分治)

题目链接

QZC树的分治论文第二道例题, 自己想了想觉得没什么思路, 后来看了论文中的解法看到中间时想到了一个线段树优化dp的解法, 论文中也提到的令dp[L] 表示从当前根结点向下经过L个crowded节点的最长路径值,在进行分治时用两个dp数组, dp1表示当前根节点前i - 1棵子树的dp值, dp2表示第i棵子树的dp值,用线段树维护dp1的值,我们每次dfs一个子树时得到dp2的值, 然后用

dp2[j] + max(dp1[k]) 0 <= k <= m - j - is[root]( m表示最多经过m个crowed节点)更新ans, 然后再用dp2更新dp1, 大致就是这样的一个做法。。。


#include <iostream>
#include <cstdio>
#include <queue>
#include <map>
#include <algorithm>
#include <cstring>
#include <cstdlib>

using namespace std;

typedef long long LL;

inline int readint() {
	char c = getchar();
	while (!isdigit(c)) c = getchar();

	int x = 0;
	while (isdigit(c)) {
		x = x * 10 + c - '0';
		c = getchar();
	}

	return x;
}

inline int lowbit(int t) {
	return t & (-t);
}

const int N = 200015;
const int M = N << 1;
const int INF = 2000000001;

#define fi first
#define se second

int n, is[N];
int head[N], next[M], to[M], W[M];
bool done[N];
int size[N], d[N];

int dp1[N], dp2[N];
int sub[N];
int E, tot, root, tdfn;
int K;
vector<int> vec;
int ans;

#define lch rt << 1, L, mid
#define rch rt << 1 | 1, mid + 1, R

struct SegmentTree {
	int maxv[N << 2];
	int n;

	void init(int n) {
		this->n = n;
		build(1, 0, n);
	}

	inline void push_up(int rt) {
		maxv[rt] = max(maxv[rt << 1], maxv[rt << 1 | 1]);
	}

	void build(int rt, int L, int R) {
		if (L == R) {
			maxv[rt] = dp1[L];
			return;
		}
		int mid = L + R >> 1;
		build(lch), build(rch);
		push_up(rt);
	}

	void update(int rt, int L, int R, int p, int v) {
		if (L == R) {
			maxv[rt] = v;
			return;
		}
		int mid = L + R >> 1;
		if (p <= mid)
			update(lch, p, v);
		else
			update(rch, p, v);

		push_up(rt);

	}

	int query(int rt, int L, int R, int l, int r) {
		if (l <= L && R <= r) {
			return maxv[rt];			
		}
		int mid = L + R >> 1;
		int res = -INF;

		if (l <= mid)
			res = max(res, query(lch, l, r));
		if (r > mid)
			res = max(res, query(rch, l, r));
		return res;
	}

	void op1(int p, int v) {
		update(1, 0, n, p, v);
	}

	int op2(int l, int r) {
		if (l > r)
			return -INF;
		return query(1, 0, n, l, r);
	}
}T;

void init() {
	for (int i = 1; i <= n; i++)
		head[i] = -1, done[i] = 0, is[i] = 0;
	E = 0;
	ans = -INF;
}

void add(int u, int v, int w) {
	W[E] = w, to[E] = v, next[E] = head[u], head[u] = E++;
	W[E] = w, to[E] = u, next[E] = head[v], head[v] = E++;
}

void gao(int u, int fa) {
	sub[tot] = u;
	int t = tot++;
	int omax = 0;
	for (int i = head[u]; i != -1; i = next[i]) {
		int v = to[i];
		if (!done[v] && v != fa) {
			gao(v, u);
			omax = max(omax, size[v]);
		}
	}
	
	size[u] = tot - t;
	if (size[u] > 1)
		d[u] = omax;
}

int center(int u) {
	tot = 0;
	gao(u, 0);
	if (tot == 1) return u;

	int key = N;
	int res;

	for (int k = 0; k < tot; k++) {
		int v = sub[k];
		d[v] = max(tot - size[v], d[v]);
		if (size[v] > 1 && d[v] < key) {
			key = d[v];
			res = v;
		}
	}

	return res;
}

void dfs(int u, int fa, int cnt, int sum) {
	
	if (cnt > K) return;	
	
	if (fa) {

		if (dp2[cnt] == -INF || dp2[cnt] < sum)	{
			if (dp2[cnt] == -INF) vec.push_back(cnt);
			dp2[cnt] = sum;
		}
	}

	for (int i = head[u]; i != -1; i = next[i]) {
		int v = to[i];
		if (v != fa && !done[v]) {
			dfs(v, u, cnt + is[v], sum + W[i]);	
		}

	}

	if (fa == root) {
		if (u != to[head[root]]) {
			for (int i = 0; i < vec.size(); i++) {
				int t = vec[i];
				if (K - is[root] - t >= 0)
					ans = max(ans, T.op2(0, K - is[root] - t) + dp2[t]);
			}	
		}

		for (int i = 0; i < vec.size(); i++) {
			int t = vec[i];
			if (dp2[t] > dp1[t]) {
				dp1[t] = dp2[t];
				T.op1(t, dp1[t]);
			}
			dp2[t] = -INF;
		}
		
		vec.clear();
	}
}

void solve(int u) {
	root = center(u);
	
	if (tot == 1) {
		done[u] = 1;
		return;
	}
	
	fill(dp1, dp1 + tot + 1, -INF);
	fill(dp2, dp2 + tot + 1, -INF);

	T.init(tot);
	
	tdfn = 0;
	dfs(root, 0, 0, 0);

	for (int i = 0; i <= tot; i++)
		if (i + is[root] <= K)
			ans = max(ans, dp1[i]);

	done[root] = 1;

	for (int i = head[root]; i != -1; i = next[i])
		if (!done[to[i]])
			solve(to[i]);
}

int main() {
	/*
	int SIZE = 256 << 20; // 256MB
	char *p = (char*)malloc(SIZE) + SIZE;
	__asm__("movl %0, %%esp\n" :: "r"(p) );*/
	
	int m, u, v, w, t;
	n = readint(), 	K = readint(), m = readint();

	init();

	for (int i = 0; i < m; i++) {
		t = readint();
		is[t] = 1;
	}

	for (int i = 0; i < n - 1; i++) {
		u = readint();
		v = readint();
		scanf("%d", &w);
		add(u, v, w);
	}
	
	solve(1);

	for (int i = 1; i <= n; i++)
		if (is[i] <= K && ans < 0) {
			ans = 0;
			break;
		}

	printf("%d\n", ans);
			
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值