[洛谷P3806]【模板】点分治1

本文介绍了一种利用点分治策略解决树上距离为k的点对查询问题的方法。通过离线处理和中心点查找,有效地解决了大规模树状结构上的复杂查询,适用于竞赛编程和算法研究。

题目大意:给定一棵有$n$个点的树,$m$个询问树上距离为$k$的点对是否存在。

题解:离线,点分治

卡点:读入边的时候不知道我在干什么。。。

 

C++ Code:

#include <cstdio>
#include <algorithm>
#define maxn 100010
#define maxk 111
const int inf = 0x3f3f3f3f;
inline int max(int a, int b) {return a > b ? a : b;}

int head[maxn], cnt;
struct Edge {
	int to, nxt, w;
} e[maxn << 1];
inline void add(int a, int b, int c) {
	e[++cnt] = (Edge) {b, head[a], c}; head[a] = cnt;
	e[++cnt] = (Edge) {a, head[b], c}; head[b] = cnt;
}

bool vis[maxn];
namespace Center_of_Gravity {
	#define n __nodenum
	int root, MIN, n;
	int sz[maxn];
	void __getroot(int u, int fa) {
		sz[u] = 1;
		int MAX = 0;
		for (int i = head[u]; i; i = e[i].nxt) {
			int v = e[i].to;
			if (v != fa && !vis[v]) {
				__getroot(v, u);
				sz[u] += sz[v];
				MAX = max(sz[v], MAX);
			} 
		}
		MAX = max(n - sz[u], MAX);
		if (MAX < MIN) MIN = MAX, root = u;
	}
	int getroot(int rt, int nodenum) {
		n = nodenum;
		MIN = inf;
		__getroot(rt, 0);
		return root;
	}
	#undef n
}
using Center_of_Gravity::getroot;

int S[maxn], tot;
void getlist(int u, int fa, int val) {
	S[++tot] = val;
	for (int i = head[u]; i; i = e[i].nxt) {
		int v = e[i].to;
		if (v != fa && !vis[v]) getlist(v, u, val + e[i].w);
	}
}
int calc(int u, int val, int k) {
	tot = 0;
	getlist(u, 0, val);
	std::sort(S + 1, S + tot + 1);
	int l = 1, r = tot, res = 0;
	while (l < r) {
		if (S[l] + S[r] > k) r--;
		else if (S[l] + S[r] < k) l++;
		else {
			int tmpl = l, tmpr = r;
			while (S[tmpl] == S[l] && tmpl <= tot) tmpl++;
			while (S[tmpr] == S[r] && tmpr) tmpr--;
			int x = r - tmpr, y = tmpl - l;
			if (S[l] != S[r]) res += x * y;
			else res += x * (x - 1) >> 1;
			l = tmpl, r = tmpr;
		}
	}
	return res;
}
int V[maxn], K[maxk];
int n, m;
void solve(int u) {
	vis[u] = true;
	for (int i = 0; i < m; i++) V[i] += calc(u, 0, K[i]);
	for (int i = head[u]; i; i = e[i].nxt) {
		int v = e[i].to;
		if (!vis[v]) {
			for (int j = 0; j < m; j++) V[j] -= calc(v, e[i].w, K[j]);
			solve(getroot(v, Center_of_Gravity::sz[v]));
		}
	}
}

int main() {
	scanf("%d%d", &n, &m);
	for (int i = 1, a, b, c; i < n; i++) {
		scanf("%d%d%d", &a, &b, &c);
		add(a, b, c);
	}
	for (int i = 0; i < m; i++) scanf("%d", K + i);
	solve(getroot(1, n));
	for (int i = 0; i < m; i++) puts(V[i] ? "AYE" : "NAY");
	return 0;
}

  

转载于:https://www.cnblogs.com/Memory-of-winter/p/9771500.html

### 洛谷 P1177 题目解析 洛谷 P1177 是一道经典的排序算法模板题,其核心目标是对给定的一组整数进行升序排列。此题适用于验证各种排序算法的性能和正确性[^3]。 #### 数据范围分析 对于本题的数据范围,已知条件如下: - 数组长度 \( N \) 满足 \( 1 \leq N \leq 10^5 \)[^3]。 - 单个数组元素满足 \( 1 \leq a_i \leq 10^9 \)。 基于上述约束,任何高效的排序算法都应具备时间复杂度为 \( O(N \log N) \),以适应大规模数据处理需求。 #### 推荐解决方案:快速排序 (Quick Sort) 快速排序是一种分治策略的经典应用,在平均情况下具有优秀的效率表现。以下是其实现逻辑: 1. **选取基准值**:从待排序序列中挑选一个元素作为基准(pivot),通常可以选择中间位置的元素。 2. **分区操作**:将小于基准值的元素放置于左侧,大于等于基准值的元素置于右侧。 3. **递归调用**:分别对左右两部分重复执行以上过程,直至区间缩小至单个元素为止。 下面是 C++ 实现代码示例: ```cpp #include <iostream> #include <vector> using namespace std; void quickSort(vector<int>& arr, int low, int high) { if (low >= high) return; // 基准值选择与分区 int pivot = arr[(low + high) / 2]; int i = low, j = high; while (i <= j) { while (arr[i] < pivot) ++i; while (arr[j] > pivot) --j; if (i <= j) { swap(arr[i], arr[j]); ++i; --j; } } // 对两侧子数组递归排序 quickSort(arr, low, j); quickSort(arr, i, high); } int main() { int n; cin >> n; vector<int> nums(n); for (auto& num : nums) cin >> num; quickSort(nums, 0, n - 1); for (const auto& num : nums) cout << num << ' '; return 0; } ``` 该方法能够有效应对题目中的大数据量场景,并保持较高的运行速度。 --- #### 其他可能方案 除了快速排序外,还可以考虑其他高效排序算法,例如归并排序(Merge Sort)或内置标准库函数 `std::sort` 来完成任务。这些替代方式同样可以达到预期效果,但在实际编码过程中需注意边界情况及内存开销等问题。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值