点分治和动态点分治( 在更)

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/xyyxyyx/article/details/90047634

点分治

例题

ciel the commander

来源:codeforces 321 C

题解:

让你给一个树每个点用小写字母标号, 要求每两个相同的标号之间的路径上至少有一个标号比他们大的点。

甚至算不上点分治,但是可以练习一下点分的求重心。

考虑贪心,每次找一个点标当前最高的号,然后一棵树分成了几棵子树,子树之间的路径肯定会经过当前标号的点,就不需要考虑了,那么递归下去就好了。

然后假如每次对于一棵子树选他的重心标号,最多只需要 O ( l o g    n ) O(log \; n) 个标号就好了。

Tree

来源:poj 1741

题解:

一棵树,求有多少组点对,距离不超过k。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
void chkMax(int &x, int y){if (x < y) x = y;}
void chkMin(int &x, int y){if (x > y) x = y;}
const int N = 1e4+10;
const int M = N<<1;
const int inf = 1e9+7;
struct G{
	int h[N], e, nxt[M], v[M], w[M];
	void clear(){
		memset(h, 0, sizeof(h));
		e = 1;
	}
	void add_dir(int _u, int _v, int _w){
		++ e;
		nxt[e] = h[_u];
		v[e] = _v;
		w[e] = _w;
		h[_u] = e;
	}
	void add_undir(int _u, int _v, int _w){
		add_dir(_u, _v, _w);
		add_dir(_v, _u, _w);
	}
}g;
int n, k, ans;
int sz[N], zx, mx, siz;
int l, r, q[N], dpt[N];
bool vis[N];

void dfs2(int u, int fa)
{
	q[++ r] = dpt[u];
	for (int i = g.h[u]; i; i = g.nxt[i]){
		int v = g.v[i];
		int w = g.w[i];
		if (v == fa || vis[v]) continue;
		dpt[v] = dpt[u]+w;
		dfs2(v, u);
	}
}

int calc(int u, int val)
{
	int ret = 0;
	l = 1; r = 0; dpt[u] = val;
	dfs2(u, 0);
	sort(q+1, q+r+1);
	while (l < r){
		if (q[l]+q[r] <= k) ret += r-l, ++ l;
		else -- r;
	}
	return ret;
}

void get_zx(int u, int fa)
{
	sz[u] = 1;
	int tmp = 0;
	for (int i = g.h[u]; i; i = g.nxt[i]){
		int v = g.v[i];
		if (v == fa || vis[v]) continue;
		get_zx(v, u);
		chkMax(tmp, sz[v]);
		sz[u] += sz[v];
	}
	chkMax(tmp, siz-sz[u]);
	if (tmp < mx) mx = tmp, zx = u;
}

void dfs1(int u)
{
	vis[u] = 1;
	ans += calc(u, 0);
	for (int i = g.h[u]; i; i = g.nxt[i]){
		int v = g.v[i];
		int w = g.w[i];
		if (vis[v]) continue;
		siz = sz[v]; mx = inf;
		ans -= calc(v, w);
		get_zx(v, u);
		dfs1(zx);
	}
}

int main()
{
	while(scanf("%d%d", &n, &k) && n && k){
		g.clear();
		ans = 0;
		for (int i = 1; i < n; ++ i){
			int x, y, z;
			scanf("%d%d%d", &x, &y, &z);
			g.add_undir(x, y, z);
		}
		memset(vis, 0, sizeof(vis));
		siz = n; mx = inf;
		get_zx(1, 0);
		dfs1(zx);
		printf("%d\n", ans);
	}
	return 0;
}

最短路径树问题

来源:bzoj4016

重建计划

来源:bzoj1758[WC2010]

动态点分治

例题

[ZJOI2007]捉迷藏

板子题。

题解链接在这里:my solution

猜你喜欢

转载自blog.csdn.net/xyyxyyx/article/details/90047634