分治学习指南

典题合集

前置芝士

点分治

适合处理大规模树上路径信息问题

树上路径分为:1.经过根节点的路径。2.不经过根节点的路径

[step]

(1)找到树的重心做根,getroot()

(2)求出子树中的各点到根的距离,getdis()

(3)对当前树统计答案,calc()

(4)分治各个子树,重复以上操作,divide()

树上距离为k的点对

[problem description]

给定一棵有 \(n\) 个点的树,询问树上距离为 \(k\) 的点对是否存在。

[input]

第一行两个数 \(n,m\)

\(2\) 到第 \(n\) 行,每行三个整数 \(u, v, w\),代表树上存在一条连接 \(u\)\(v\) 边权为 \(w\) 的路径。

接下来 \(m\) 行,每行一个整数 \(k\),代表一次询问。

[output]

对于每次询问输出一行一个字符串代表答案,存在输出 AYE,否则输出 NAY

[a.in]

2 1
1 2 2
2

[a.out]

AYE

[datas]

\(1 \leq n\leq 10^4\)\(1 \leq m\leq 100\)\(1 \leq k \leq 10^7\)\(1 \leq u, v \leq n\)\(1 \leq w \leq 10^4\)

[solved]

const int N = 10010;
const int INF = 10000010;
struct edge {
	int v, w, ne;
} e[N * 2];
int h[N], idx;
int del[N], sz[N], mxs, sum, root;

int dis[N], d[N], cnt;
int ans[N], q[INF], judge[INF];
int n, m, ask[N];

void add(int u, int v, int w) {
	e[++idx] = {v, w, h[u]};
	h[u] = idx;
}
//寻找重心
void getroot(int u, int fa) {
	sz[u] = 1;
	int s = 0;
	for (int i = h[u]; i; i = e[i].ne) {
		int v = e[i].v;
		if (v == fa || del[v]) continue;
		getroot(v, u);
		sz[u] += sz[v];
		s = max(s, sz[v]);
	}
	s = max(s, sum - sz[u]);
	if (s < mxs) mxs = s, root = u;
}
//记录各个点到根节点的距离
void getdis(int u, int fa) {
	dis[++cnt] = d[u];
	for (int i = h[u]; i; i = e[i].ne) {
		int v = e[i].v;
		if (v == fa || del[v]) continue;
		d[v] = d[u] + e[i].w;
		getdis(v, u);
	}
}
void calc(int u) {
	del[u] = judge[0] = 1;
	int p = 0;
	for (int i = h[u]; i; i = e[i].ne) {
		int v = e[i].v;
		if (del[v]) continue;
		cnt = 0;
		d[v] = e[i].w;
		//求出子树v的各点到u的距离
		getdis(v, u);
		for (int j = 1; j <= cnt; j++) {
			for (int k = 1; k <= m; k++) {
				if (ask[k] >= dis[j])
					ans[k] |= judge[ask[k] - dis[j]];
			}
		}
		//记录合法路径
		for (int j = 1; j <= cnt; j++) {
			if (dis[j] < INF)
				q[++p] = dis[j], judge[dis[j]] = 1;
		}
	}
	for (int i = 1; i <= p; i++) judge[q[i]] = 0;
}
void divide(int u) {
	calc(u);
	for (int i = h[u]; i; i = e[i].ne) {
		int v = e[i].v;
		if (del[v]) continue;
		mxs = sum = sz[v];
		getroot(v, 0);
		divide(root);
	}
}

void solve() {
	//freopen("a.in","r",stdin);
	//freopen("a.out","w",stdout);
	cin >> n >> m;
	for (int i = 1, x, y, w; i < n; i++) {
		cin >> x >> y >> w;
		add(x, y, w);
		add(y, x, w);
	}
	for (int i = 1; i <= m; i++) {
		cin >> ask[i];
	}
	mxs=sum=n;
	getroot(1,0);
	getroot(root,0);
	divide(root);
	for (int i = 1; i <= m; i++) {

		if (ans[i])cout << "AYE" << endl;
		else cout<<"NAY"<<endl;
	}
}
posted @ 2023-11-11 12:02  White_Sheep  阅读(7)  评论(0编辑  收藏  举报