E. Xenia and Tree 分块 + LCA
http://codeforces.com/contest/342/problem/E
如果把询问1存起来,每到sqrt(m)的时候再处理一次。
那么总复杂度就是msqrt(m)的。
把要变颜色的节点存起来,可以同时一次O(n)的bfs
然后就是LCA了。LCA需要倍增的做法。这题真的是个好题。。
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <assert.h>
#define IOS ios::sync_with_stdio(false)
using namespace std;
#define inf (0x3f3f3f3f)
typedef long long int LL; #include <iostream>
#include <sstream>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <string>
const int maxn = 2e5 + ;
int col[maxn];
struct node {
int u, v;
int tonext;
}e[maxn];
int first[maxn];
int num;
void add(int u, int v) {
++num;
e[num].u = u;
e[num].v = v;
e[num].tonext = first[u];
first[u] = num;
}
int tot[maxn];
int lentot;
int dp[maxn];
struct bfsnode {
int cur, cnt;
bfsnode(int a, int b) : cur(a), cnt(b) {}
};
queue<struct bfsnode>que;
bool vis[maxn];
void bfs() {
memset(vis, false, sizeof vis);
for (int i = ; i <= lentot; ++i) {
que.push(bfsnode(tot[i], ));
dp[tot[i]] = ;
}
while (!que.empty()) {
struct bfsnode t = que.front();
que.pop();
for (int i = first[t.cur]; i; i = e[i].tonext) {
int v = e[i].v;
if (vis[v]) continue;
if (dp[v] <= t.cnt + ) continue;
vis[v] = true;
dp[v] = t.cnt + ;
que.push(bfsnode(v, t.cnt + ));
}
}
}
int ansc[maxn][], deep[maxn], fa[maxn];
void init_LCA(int cur) {
ansc[cur][] = fa[cur]; //跳1步,那么祖先就是爸爸
for (int i = ; i <= ; ++i) { //倍增思路,递归处理
ansc[cur][i] = ansc[ansc[cur][i - ]][i - ];
}
for (int i = first[cur]; i; i = e[i].tonext) {
int v = e[i].v;
if (v == fa[cur]) continue;
fa[v] = cur;
deep[v] = deep[cur] + ;
init_LCA(v);
}
}
int LCA(int x, int y) {
if (deep[x] < deep[y]) swap(x, y); //需要x是最深的
for (int i = ; i >= ; --i) { //从大到小枚举,因为小的更灵活
if (deep[ansc[x][i]] >= deep[y]) { //深度相同,走进去就对了。
x = ansc[x][i];
}
}
if (x == y) return x;
for (int i = ; i >= ; --i) {
if (ansc[x][i] != ansc[y][i]) { //走到第一个不等的地方,
x = ansc[x][i];
y = ansc[y][i];
}
}
return ansc[x][]; //再跳一步就是答案
}
int dis[maxn];
void dfs(int cur, int step) {
dis[cur] = min(dis[cur], step);
for (int i = first[cur]; i; i = e[i].tonext) {
int v = e[i].v;
if (vis[v]) continue;
vis[v] = true;
dfs(v, step + );
}
}
void work() {
int n, m;
scanf("%d%d", &n, &m);
for (int i = ; i <= n - ; ++i) {
int u, v;
scanf("%d%d", &u, &v);
add(u, v);
add(v, u);
} memset(dis, 0x3f, sizeof dis);
dis[] = ;
vis[] = true;
dfs(, ); memset(dp, 0x3f, sizeof dp);
col[] = ;
tot[++lentot] = ;
bfs();
lentot = ; fa[] = ;
deep[] = ;
init_LCA(); int magic = (int)sqrt(m);
for (int i = ; i <= m; ++i) {
int flag, which;
scanf("%d%d", &flag, &which);
if (flag == ) {
tot[++lentot] = which;
} else {
int ans = dp[which];
for (int i = ; i <= lentot; ++i) {
int haha = LCA(which, tot[i]);
ans = min(ans, dis[which] + dis[tot[i]] - * dis[haha]);
}
printf("%d\n", ans);
}
if (lentot >= magic) {
bfs();
lentot = ;
}
}
} int main() {
#ifdef local
freopen("data.txt", "r", stdin);
// freopen("data.txt", "w", stdout);
#endif
work();
return ;
}
LCA是用在树中的,图的不行。
LCA[u][v]表示节点u和v的最近公共祖先,也是深度最大祖先,深度越大的话,证明离u和v越近嘛。这个算法基于dfs的回溯和并查集实现。dfs的时候,搜索到叶子节点(没有儿子)的时候,得到LCA[u][u]=u和LCA[u][fa]=fa,然后,返回到他爸爸那里,并查集合并,f[u]=fa;表明u的爸爸是fa,所以这个时候并查集是向左看齐的,merge(u,v),u只能是爸爸。复杂度O(n²)的算法,能求出整棵树的所有LCA[i][j]值。并查集那里有点奇葩,它也用作了标记数组的作用,所以一开始的并查集,全部是0。
void dfs(int u) {
f[u] = u; //首先自己是一个集合
for (int i = first[u]; i; i = e[i].next) {
int v = e[i].v;
if (f[v] == 0) {
dfs(v);
merge(u, v);
}
}
for (int i = 1; i <= n; i++) { //遍历每一个点
if (f[i]) { //已经确定过的,就更新LCA
LCA[u][i] = LCA[i][u] = find(i);
}
}
return ;
}
O(n+Q)算法,用邻接表存取所有询问,要询问的再处理即可,注意去重操作。
void dfs(int u) {
f[u] = u; //首先自己是一个集合
for (int i = first[u]; i; i = e[i].next) {
int v = e[i].v;
if (f[v] == 0) {
dfs(v);
merge(u, v);
}
}
for (int i = first_query[u]; i; i = query[i].next) {
int v = query[i].v;
if (f[v]) { //确定过的话,并且有要求查询
//要求查询的话这个query保存着,first_query[u]就证明有没了
//因为插边插了两次,这里要去重。用id保存答案即可
ans[query[i].id] = find(v);
}
}
return ;
}
LCA倍增算法。
设ansc[cur][i]表示从cur这个节点跳2i步到达的祖先是谁。记录深度数组deep[cur]。深度从0开始,然后算LCA的时候就先把他们弄到同一深度,然后一起倍增。
Hint:ans[root][3]是自己,都是root。开始的时候fa[root] = root。deep[root] = 0;
一般这课树是双向的,因为可能结合bfs来做题,所以需要判断不能走到爸爸那里。
int ansc[maxn][25], deep[maxn], fa[maxn];
void init_LCA(int cur) {
ansc[cur][0] = fa[cur]; //跳1步,那么祖先就是爸爸
for (int i = 1; i <= 24; ++i) { //倍增思路,递归处理
ansc[cur][i] = ansc[ansc[cur][i - 1]][i - 1];
}
for (int i = first[cur]; i; i = e[i].tonext) {
int v = e[i].v;
if (v == fa[cur]) continue;
fa[v] = cur;
deep[v] = deep[cur] + 1;
init_LCA(v);
}
}
int LCA(int x, int y) {
if (deep[x] < deep[y]) swap(x, y); //需要x是最深的
for (int i = 24; i >= 0; --i) { //从大到小枚举,因为小的更灵活
if (deep[ansc[x][i]] >= deep[y]) { //深度相同,走进去就对了。
x = ansc[x][i];
}
}
if (x == y) return x;
for (int i = 24; i >= 0; --i) {
if (ansc[x][i] != ansc[y][i]) { //走到第一个不等的地方,
x = ansc[x][i];
y = ansc[y][i];
}
}
return ansc[x][0]; //再跳一步就是答案
}
E. Xenia and Tree 分块 + LCA的更多相关文章
- E. Xenia and Tree 解析(思維、重心剖分)
Codeforce 342 E. Xenia and Tree 解析(思維.重心剖分) 今天我們來看看CF342E 題目連結 題目 給你一棵樹,有兩種操作,把某點標成紅色或者查詢離某點最近的紅點有多遠 ...
- codeforces 342E :Xenia and Tree
Description Xenia the programmer has a tree consisting of n nodes. We will consider the tree nodes i ...
- Codeforces Round #329 (Div. 2) D. Happy Tree Party LCA/树链剖分
D. Happy Tree Party Bogdan has a birthday today and mom gave him a tree consisting of n vertecie ...
- 【SPOJ】10628. Count on a tree(lca+主席树+dfs序)
http://www.spoj.com/problems/COT/ (速度很快,排到了rank6) 这题让我明白了人生T_T 我知道我为什么那么sb了. 调试一早上都在想人生. 唉. 太弱. 太弱. ...
- LeetCode Lowest Common Ancestor of a Binary Search Tree (LCA最近公共祖先)
题意: 给一棵二叉排序树,找p和q的LCA. 思路: 给的是BST(无相同节点),那么每个节点肯定大于左子树中的最大,小于右子树种的最小.根据这个特性,找LCA就简单多了. 分三种情况: (1)p和q ...
- 2018 ICPC青岛网络赛 B. Red Black Tree(倍增lca好题)
BaoBao has just found a rooted tree with n vertices and (n-1) weighted edges in his backyard. Among ...
- HDU 4912 Paths on the tree(LCA+贪心)
题目链接 Paths on the tree 来源 2014 多校联合训练第5场 Problem B 题意就是给出m条树上的路径,让你求出可以同时选择的互不相交的路径最大数目. 我们先求出每一条路径 ...
- HDU 5274 Dylans loves tree(LCA+dfs时间戳+成段更新 OR 树链剖分+单点更新)
Problem Description Dylans is given a tree with N nodes. All nodes have a value A[i].Nodes on tree i ...
- HDU 6394 Tree 分块 || lct
Tree 题意: 给你一颗树, 每一个节点都有一个权值, 如果一个石头落在某个节点上, 他就会往上跳这个的点的权值步. 现在有2种操作, 1 把一个石头放在 x 的位置 询问有跳几次才跳出这棵树, 2 ...
随机推荐
- 小程序 swiper banner 图片 居中
var imgUrlApp = getApp().globalData.imgUrlApp; Page({ /** * 页面的初始数据 */ data: { indicatorDots: true, ...
- 使用forever让node.js持久运行
何为forever?forever可以看做是一个nodejs的守护进程,能够启动,停止,重启我们的app应用. npm install forever -g #安装 forever start app ...
- mysql数据库隔离级别及其原理、Spring的7种事物传播行为
一.事务的基本要素(ACID) 1.原子性(Atomicity):事务开始后所有操作,要么全部做完,要么全部不做,不可能停滞在中间环节.事务执行过程中出错,会回滚到事务开始前的状态,所有的操作就像没有 ...
- 使用delphi 开发多层应用(十六)使用XMLRPC 实现basic4android 远程调用RTC服务(讲述了RTC的特点,其底层通讯协议是自己封装SOCK 库,与kbmmw 的适合场合不完全一样)
RealThinClient (以下简称RTC) 也是一款delphi 多层开发的框架,由于其底层通讯协议是自己封装SOCK 库,抛弃了 大家诟病的indy,因此表现的非常稳定,效率也非常高, ...
- 【NOIP2017Day1T1】 小凯的疑惑
[题目链接] 点击打开链接 [算法] px + qy不能表示的最大整数为 pq - p - q 证明见这篇博客,过程很详细,推荐阅读 : https://blog.csdn.net/qwerty112 ...
- 【SDOI2012】 Longgue的问题
[题目链接] 点击打开链接 [算法] gcd(i,n)是n的约数 不妨设gcd(i,n) = d 考虑枚举d和gcd(i,n) = d有多少个 gcd(i,n) = d gcd(i/d,n/d) = ...
- 【Codeforces 947B】 Producting Snow
[题目链接] 点击打开链接 [算法] 前缀和 + 堆 [代码] #include<bits/stdc++.h> using namespace std; typedef long long ...
- 洛谷P3830 [SHOI2012]随机树——概率期望
题目:https://www.luogu.org/problemnew/show/P3830 询问1:f[x]表示有x个叶节点的树的叶节点平均深度: 可以把被扩展的点的深度看做 f[x-1] ,于是两 ...
- laravel 自定义分页 offset 和 limit 的使用
laravel 本身有一个自带的快速分页方法 paginate,只需要传入每页显示多少条数据就可以 了,但是如果想使用自定义从哪里开始呢,这时候就可以使用offset 和 limit 的组合,offs ...
- 2.28 MapReduce在实际应用中常见的优化
一.优化的点 Reduce Task Number Map Task输出压缩 Shuffle Phase 参数 map.reduce分配的虚拟CPU 二.Reduce Task Number Redu ...