【LCA最近公共祖先】 poj1470 Closest Common Ancestors

本文讨论了一次编程过程中因将数组头节点的名称错误输入而导致的时间浪费问题,并介绍了如何通过tarjan算法进行离线操作来解决此类问题。文章详细解释了tarjan算法的实现过程,包括边的添加、查询的构建、深度优先搜索以及LCA(最近公共祖先)的查找。同时,文中还展示了如何通过tarjan算法优化节点间的连接关系,减少不必要的计算,提高程序效率。

昨晚写错了,把数组名head2写成了head,浪费了一个来小时。。。

tarjan离线搞,不言语:

#include <cstdio>
#include <cstring>
#include <string>
#include <vector>
#include <algorithm>
#include <map>
using namespace std;

const int MAX = 1024;
struct edge {
	int from;
	int to;
	int next;
	edge(int _from, int _to, int _next) {
		from = _from;
		to = _to;
		next = _next;
	}
};
struct query {
	int u;
	int v;
	int id;
	int next;
	query(int _u, int _v, int _id, int _next) {
		u = _u;
		v = _v;
		id = _id;
		next = _next;
	}
};
vector<edge> G;
vector<query> my;
int head[MAX], head2[MAX];
int LCA[MAX*MAX];
int in[MAX]; //in degree
int father[MAX];
int n, m, root;

inline void add_edge(int a, int b) {
	G.push_back(edge(a, b, head[a]));
	head[a] = G.size() - 1;
}

inline void add_query(int a, int b, int id) {
	my.push_back(query(a, b, id, head2[a]));
	head2[a] = my.size() - 1;
}

inline int read() {
	char ch;
	while ((ch = getchar()) < '0' || ch > '9') {

	}
	int x = ch - '0';
	while ((ch = getchar()) >= '0' && ch <= '9') {
		x = (x << 3) + (x << 1) + ch - '0';
	}
	return x;
}

int find(int x) {
	return x == father[x] ? x : father[x] = find(father[x]);
}

void tarjan(int u) {
	father[u] = u;
	for (int j = head2[u]; ~j; j = my[j].next) {
		//printf("vis %d\n", my[j].v);
		if (~father[my[j].v]) {
			LCA[my[j].id] = find(my[j].v);
			//printf("LCA[%d] = %d\n", my[j].id, LCA[my[j].id]);
		}
	}
	for (int j = head[u]; ~j; j = G[j].next) {
		if (father[G[j].to] == -1) {
			tarjan(G[j].to);
			father[G[j].to] = u;
		}
	}
}

int main() {
	//freopen("1470.in", "r", stdin);
	while (~scanf(" %d", &n)) {
		memset(father, -1, sizeof(father));
		memset(head, -1, sizeof(head));
		memset(head2, -1, sizeof(head2));
		memset(in, 0, sizeof(in));
		G.clear();
		my.clear();
		
		int u, v;
		for (int i = 1; i <= n; ++i) {
			u = read();
			int c = read();
			while (c--) {
				v = read();
				add_edge(u, v);
				//add_edge(v, u);
				++in[v];
			}
		}
		
		m = read();
		for (int i = 1; i <= m; ++i) {
			u = read();
			v = read();
			add_query(u, v, i);
			add_query(v, u, i);
		}
		
		for (int i = 1; i <= n; ++i) {
			if (in[i] == 0) {
				root = i;
				break;
			}
		}

		tarjan(root);
		map<int, int> ans;
		for (int i = 1; i <= m; ++i) {
			ans[LCA[i]]++;
		}
		for (map<int, int>::iterator it = ans.begin(); it != ans.end(); ++it) {
			printf("%d:%d\n", it->first, it->second);
		}
		if (m) getchar();
	}
	return 0;
}


<think>我们正在讨论如何阻止DOM事件冒泡默认行为。用户的问题是关于JavaScript中stopPropagationpreventDefault的用法。 根据引用[1],我们有一个阻止默认行为的例子:在touchmove事件中调用event.preventDefault()可以阻止默认的滑动行为。 引用[2]展示了事件捕获的例子,但并没有直接涉及阻止冒泡。 引用[3]提到事件解绑的注意事项,并指出匿名函数无法进行事件解绑,因为解绑时需要同一个函数引用。 用户的问题集中在两个方法: 1. event.stopPropagation() - 阻止事件冒泡(或捕获)传播 2. event.preventDefault() - 阻止事件默认行为 我们需要解释这两个方法的用法区别,并给出示例。 步骤: 1. 解释stopPropagation:用于阻止事件在DOM树中进一步传播(包括冒泡阶段捕获阶段)。但注意,它不会阻止事件默认行为。 2. 解释preventDefault:用于取消事件默认行为(如果该行为可取消)。例如,点击链接的默认行为是跳转,表单提交的默认行为是提交表单等。 3. 注意:有些事件没有默认行为,所以调用preventDefault不会有任何效果。 示例代码: 阻止事件冒泡的示例: document.getElementById('child').addEventListener('click', function(event) { event.stopPropagation(); // 阻止事件冒泡到父元素 // 其他处理 }); document.getElementById('parent').addEventListener('click', function() { // 如果不阻止冒泡,点击子元素时这里也会触发 }); 阻止默认行为的示例: document.getElementById('myLink').addEventListener('click', function(event) { event.preventDefault(); // 阻止链接跳转 // 其他处理,比如异步加载内容 }); 4. 同时使用两个方法的情况: document.getElementById('myButton').addEventListener('click', function(event) { event.preventDefault(); // 阻止表单提交的默认行为(假设是type="submit"的按钮) event.stopPropagation(); // 阻止事件冒泡,避免触发父元素的点击事件 // 其他处理 }); 5. 注意:在事件处理函数中,我们可以通过return false来同时达到阻止冒泡阻止默认行为的效果吗? 在jQuery中,return false会同时调用preventDefaultstopPropagation。但是在原生JavaScript中,return false只会阻止默认行为,而不会阻止事件冒泡。因此,在原生JS中,我们不应该依赖return false阻止冒泡。 6. 另外,引用[3]提到,使用匿名函数时无法解绑事件,因为需要同一个函数引用。因此,在需要解绑事件的场景,我们应该使用具名函数。 7. 我们可以引用[1]中的例子来展示preventDefault的用法,引用[3]来强调使用具名函数以便解绑。 8. 生成相关问题。 回答结构: 1. 介绍stopPropagationpreventDefault的作用。 2. 分别给出示例代码。 3. 讨论同时使用的情况。 4. 注意事项(如匿名函数解绑问题,return false的行为差异等)。 5. 生成相关问题。 注意:所有行内代码变量用$...$,独立公式用$$...$$(但本问题可能涉及较少数学公式,更多代码示例)。但根据指令,数学表达式需要用LaTeX,代码用代码块。</think>### JavaScript 中阻止事件冒泡默认行为的方法 在 JavaScript 中,`stopPropagation()` `preventDefault()` 是两个核心的事件控制方法: #### 1. **`event.stopPropagation()` - 阻止事件冒泡** ```javascript element.addEventListener('click', function(event) { event.stopPropagation(); // 阻止事件向父元素传播 }); ``` **作用**: 阻止事件在 DOM 树中向上冒泡(从子元素到祖先元素),但不会影响当前元素的其他事件监听器执行[^2]。 **应用场景**: - 点击子元素时不触发父元素的点击事件 - 避免事件委托中的意外触发 #### 2. **`event.preventDefault()` - 阻止默认行为** ```javascript link.addEventListener('click', function(event) { event.preventDefault(); // 阻止链接跳转 }); ``` **作用**: 取消浏览器对事件默认响应行为(如链接跳转、表单提交、滚动行为等)[^1]。 **应用场景**: - 阻止表单自动提交 - 禁用触摸滚动(移动端) - 自定义超链接行为 --- ### ⚠ 注意事项 1. **同时使用两个方法** ```javascript button.addEventListener('click', (event) => { event.preventDefault(); // 阻止默认提交 event.stopPropagation(); // 阻止冒泡到父容器 }); ``` 2. **事件解绑要求** 使用具名函数而非匿名函数,否则无法通过 `removeEventListener()` 解绑[^3]: ```javascript // 正确做法(具名函数) function handleClick(event) { event.stopPropagation(); } element.addEventListener('click', handleClick); element.removeEventListener('click', handleClick); ``` 3. **事件捕获阶段** 在捕获阶段(第三个参数设为 `true`)调用 `stopPropagation()` 会阻止事件向下传递: ```javascript parent.addEventListener('click', (event) => { event.stopPropagation(); // 阻止事件传递到子元素 }, true); ``` 4. **兼容性** 所有现代浏览器均支持这两个方法,包括移动端浏览器[^1]。 --- ### 完整示例 ```html <div id="parent"> 父区域 <a id="child" href="https://example.com">子链接</a> </div> <script> const parent = document.getElementById('parent'); const child = document.getElementById('child'); // 父元素监听(冒泡阶段) parent.addEventListener('click', () => { console.log('父元素被点击'); }); // 子元素监听 child.addEventListener('click', (event) => { event.preventDefault(); // 阻止链接跳转 event.stopPropagation(); // 阻止事件冒泡到父元素 console.log('子元素被点击,但不会跳转且不触发父元素'); }); </script> ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值