区间最深LCA
求编号在区间[l, r]之间的两两lca的深度最大值。
例题。
解:口胡几种做法。前两种基于莫队,第三种是启发式合并 + 扫描线,第四种是lct + 线段树。
①:
有个结论就是这个答案一定是点集中DFS序相邻的两个点的lca。于是开个数据结构,以DFS序为key维护点集,找前驱后继,额外用一个数据结构维护所有lca的深度,取最大值即可。外面套莫队就做完了。
实现上这两个数据结构都可以用树状数组。
#include <bits/stdc++.h>
#define out(a) std::cerr << #a" = " << a << std::endl;
template <class T> inline void read(T &x) {
x = ;
char c = getchar();
while(c < '' || c > '') c = getchar();
while(c >= '' && c <= '') {
x = x * + c - ;
c = getchar();
}
return;
}
const int N = ;
struct Edge {
int nex, v;
}edge[N << ]; int tp;
int e[N], pos[N], pos2[N], num, num2, fr[N], ans[N], id[N], ST[N << ][], d[N], pw[N << ], n;
/*std::set<int> st; /// save pos
std::set<int>::iterator it;*/
/*std::multiset<int> Ans;
std::multiset<int>::iterator it2;*/
namespace Ans {
int ta[N], cnt;
inline void add(int i) {
++cnt;
// printf("Ans : add : %d \n", i);
for(; i <= n; i += i & (-i)) {
ta[i]++;
}
return;
}
inline void del(int i) {
--cnt;
// printf("Ans : del : %d \n", i);
for(; i <= n; i += i & (-i)) {
ta[i]--;
}
return;
}
inline int getMax() {
int ans = , k = cnt, t = pw[n];
while(t >= ) {
if(((ans | ( << t)) <= n) && ta[ans | ( << t)] < k) {
k -= ta[ans | ( << t)];
ans |= ( << t);
}
t--;
}
return ans + ;
}
}
namespace ta {
int ta[N], cnt;
inline void add(int i) {
++cnt;
// printf("ta : add : %d \n", i);
for(; i <= n; i += i & (-i)) {
ta[i]++;
}
return;
}
inline void del(int i) {
--cnt;
// printf("ta : del : %d \n", i);
for(; i <= n; i += i & (-i)) {
ta[i]--;
}
return;
}
inline int getKth(int k) {
// printf("ta : Kth %d : ", k);
int ans = , t = pw[n];
while(t >= ) {
if(((ans | ( << t)) <= n) && ta[ans | ( << t)] < k) {
k -= ta[ans | ( << t)];
ans |= ( << t);
}
t--;
}
// printf("%d cnt = %d \n", ans + 1, cnt);
return ans + ;
}
inline int getSum(int i) {
int ans = ;
for(; i; i -= i & (-i)) {
ans += ta[i];
}
return ans;
}
}
struct Ask {
int l, r, id;
inline bool operator <(const Ask &w) const {
if(fr[l] != fr[w.l]) return l < w.l;
return r < w.r;
}
}ask[N];
inline void add(int x, int y) {
tp++;
edge[tp].v = y;
edge[tp].nex = e[x];
e[x] = tp;
return;
}
void DFS_1(int x, int f) {
d[x] = d[f] + ;
// printf("x = %d \n", x);
pos[x] = ++num;
id[num] = x;
pos2[x] = ++num2;
ST[num2][] = x;
for(int i = e[x]; i; i = edge[i].nex) {
int y = edge[i].v;
if(y == f) continue;
DFS_1(y, x);
ST[++num2][] = x;
}
return;
}
inline void prework() {
register int i, j;
for(i = ; i <= num2; i++) pw[i] = pw[i >> ] + ;
for(j = ; j <= pw[num2]; j++) {
for(i = ; i + ( << j) - <= num2; i++) {
if(d[ST[i][j - ]] < d[ST[i + ( << (j - ))][j - ]])
ST[i][j] = ST[i][j - ];
else
ST[i][j] = ST[i + ( << (j - ))][j - ];
}
}
return;
}
inline int lca(int x, int y) {
x = pos2[x];
y = pos2[y];
if(x > y) std::swap(x, y);
int t = pw[y - x + ];
if(d[ST[x][t]] < d[ST[y - ( << t) + ][t]])
return ST[x][t];
else
return ST[y - ( << t) + ][t];
}
inline void add(int x) {
// std::cerr << "------------ add " << x << std::endl;
ta::add(pos[x]);
// std::cerr << "111 \n";
int rk = ta::getSum(pos[x]);
// std::cerr << "222 \n";
int y = , z = ;
if(rk != ) {
y = id[ta::getKth(rk - )];
}
if(rk != ta::cnt) {
z = id[ta::getKth(rk + )];
}
// std::cerr << "333 \n";
// out(y); out(z);
if(y) Ans::add(d[lca(x, y)]);
if(z) Ans::add(d[lca(x, z)]);
if(y && z) Ans::del(d[lca(y, z)]);
return;
}
inline void del(int x) {
// std::cerr << "------------ del " << x << std::endl;
int rk = ta::getSum(pos[x]);
int y = , z = ;
if(rk != ) {
y = id[ta::getKth(rk - )];
}
if(rk != ta::cnt) {
z = id[ta::getKth(rk + )];
}
if(y) Ans::del(d[lca(x, y)]);
if(z) Ans::del(d[lca(x, z)]);
if(y && z) Ans::add(d[lca(y, z)]);
ta::del(pos[x]);
return;
}
int main() {
freopen("lca.in", "r", stdin);
freopen("lca.out", "w", stdout);
register int i;
int m;
read(n); read(m);
for(int i = , x, y; i < n; i++) {
read(x); read(y);
add(x, y); add(y, x);
}
DFS_1(, );
prework();
int T = n / sqrt(m);
for(i = ; i <= n; i++) {
fr[i] = (i - ) / T + ;
}
for(i = ; i <= m; i++) {
read(ask[i].l); read(ask[i].r);
ask[i].id = i;
}
std::sort(ask + , ask + m + );
int l = , r = ; ta::add();
for(i = ; i <= m; i++) {
/*if(i % 1 == 0) {
std::cerr << "i = " << i << std::endl;
}*/
// printf("i = %d [%d %d] ask [%d %d] \n", i, l, r, ask[i].l, ask[i].r);
while(ask[i].l < l) {
add(--l);
}
while(r < ask[i].r) {
add(++r);
}
while(l < ask[i].l) {
del(l++);
}
while(ask[i].r < r) {
del(r--);
}
// printf("Ans = %d \n", Ans::getMax());
ans[ask[i].id] = Ans::getMax();
}
for(i = ; i <= m; i++) {
printf("%d\n", ans[i]);
}
return ;
}
代码
②:
换反回滚莫队(只有删除),第一个数据结构换成链表。可以发现每次删掉一些节点然后按照原顺序插回来的话,可以做到O(1)前驱后继。然后第二个数据结构换成值域分块,可以O(1)修改√查询。
③:
这是一个nlog2n的做法。
考虑点x何时会被作为lca,显然就是在合并子树的时候,两个子树中各有一个点被选。
这个启发式一下,枚举小的那个子树,于是对于枚举到的每个点y,在另一棵子树中每个点被选都会导致x成为一次lca。
考虑到查询的编号总是连续的,于是只要在另一个子树中找到y的前驱后继即可。如果别的点和y有贡献,那么前驱和后继也一定有贡献。
于是我们有了O(nlogn)个点对和m个询问,全部按照左端点排序,从大到小枚举左端点,然后把点对加入。
开一个数据结构维护右端点恰为i的答案。于是答案就是一段前缀的最大值,树状数组即可。
#include <bits/stdc++.h> using namespace std; const int maxn = ;
int n,m,head[maxn],to[maxn * ],nextt[maxn * ],tot = ,deep[maxn],cnt,ans[maxn],c[maxn];
set<int> S[maxn]; struct node
{
int x,y,v;
}e[],q[]; inline int read()
{
int x = ;
char ch = getchar();
while (ch < '' || ch > '')
ch = getchar();
while (ch >= '' && ch <= '')
{
x = (x << ) + (x << ) + ch - '';
ch = getchar();
}
return x;
} inline void add(int x,int y)
{
to[tot] = y;
nextt[tot] = head[x];
head[x] = tot++;
} inline void Merge(int x,int y)
{
if (S[x].size() < S[y].size())
S[x].swap(S[y]);
for (set<int>::iterator it = S[y].begin(); it != S[y].end(); ++it)
{
int temp = (*it);
S[x].insert(temp);
set<int>::iterator it2 = S[x].find(temp);
if (it2 != S[x].begin())
{
--it2;
++cnt;
e[cnt].x = (*it2);
e[cnt].y = temp;
e[cnt].v = deep[x];
}
it2 = S[x].find(temp);
++it2;
if (it2 != S[x].end())
{
++cnt;
e[cnt].x = temp;
e[cnt].y = (*it2);
e[cnt].v = deep[x];
}
}
S[y].clear();
} void dfs(int u,int faa)
{
deep[u] = deep[faa] + ;
S[u].insert(u);
for (register int i = head[u];i;i = nextt[i])
{
int v = to[i];
if (v == faa)
continue;
dfs(v,u);
Merge(u,v);
}
} inline void Add(int x,int v)
{
while (x <= n)
{
c[x] = max(c[x],v);
x += x & (-x);
}
} inline int Query(int x)
{
int res = ;
while (x)
{
res = max(res,c[x]);
x -= x & (-x);
}
return res;
} inline bool cmp(node a,node b)
{
return a.x > b.x;
} int main()
{
freopen("lca.in","r",stdin);
freopen("lca.out","w",stdout);
n = read(),m = read();
for (register int i = ; i < n; i++)
{
int x,y;
x = read(),y = read();
add(x,y);
add(y,x);
}
dfs(,);
for (register int i = ; i <= m; i++)
{
q[i].x = read(),q[i].y = read();
q[i].v = i;
}
sort(q + ,q + + m,cmp);
sort(e + ,e + + cnt,cmp);
int cur = ;
for (register int i = ; i <= m; i++)
{
while (cur <= cnt && e[cur].x >= q[i].x)
{
Add(e[cur].y,e[cur].v);
cur++;
}
ans[q[i].v] = Query(q[i].y);
}
for (register int i = ; i <= m; i++)
printf("%d\n",ans[i]); return ;
}
代码
可以用树套树做到在线。
④:
这是一个上界nlog²n的做法。参考资料。
区间最深LCA的更多相关文章
- 区间节点的lca
题目hdu5266 分析:多节点的LCA就是dfs序中最大最小两个节点的LCA.所以只要每次维持给出节点的dfs序的最大最小,然后就是两点的LCA 代码: rmq的st+lca的倍增 #include ...
- HDU 6065 RXD, tree and sequence (LCA DP)
RXD, tree and sequence Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 524288/524288 K (Java ...
- 2016-2017 ACM-ICPC, NEERC, Northern Subregional Contest
A. Anniversary Cake 随便挑两个点切掉就好了. #include<bits/stdc++.h> using namespace std; const int Maxn=2 ...
- 【BZOJ】3757: 苹果树
http://www.lydsy.com/JudgeOnline/problem.php?id=3757 题意:n个节点的树,每个点有一种颜色.现有m种询问,每次询问x y a b表示x到y的路径上颜 ...
- Codeforces 1062E 题解
给出一棵有根树,1为根结点,接下来q次询问,每次给出一个[l,r]区间,现在允许删掉[l,r]区间内任何一个点,使得所有点的最近公共祖先的深度尽可能大,问删掉的点是哪个点,深度最大是多少. 做法: 线 ...
- CSPS模拟94
我好菜啊...... %%%迪神AK 虽然考试成绩不太好,但至少能想到正解了,也不会菜到打不出暴力. T1:想了半天不会,发现直接打高精可以拿到80分,就赶紧码完扔了,结果正解是利用double避免了 ...
- HDU 5266 pog loves szh III(区间LCA)
题目链接 pog loves szh III 题意就是 求一个区间所有点的$LCA$. 我们把$1$到$n$的$DFS$序全部求出来……然后设$i$的$DFS$序为$c[i]$,$pc[i]$为$c ...
- hdu 5044 树区间操作最后输出/ lca+dfs
题意:一棵树,俩种操作:1 有路径上的全部点加vi,2全部边加vi. 先离线求出全部询问的lca,再遍历询问一次,点+vi,lca-2*vi ,最后dfs从叶子扫上来一次,最后再祖先点补上就可以.用了 ...
- [hdu5266]区间LCA
题意:给一棵树,求节点L,L+1,...R的最近公共祖先 思路:先对树dfs一下,从根1出发,经过每条边时记录一下终点和到达这个点的时间截,令r[u]表示到达u这个节点的最早时间截,t[x]表示在时间 ...
随机推荐
- Python 中关于 round 函数的小坑
参考: http://www.runoob.com/w3cnote/python-round-func-note.html
- Netty ByteBuf和Nio ByteBuffer
参考https://blog.csdn.net/jeffleo/article/details/69230112 一.简介 Netty中引入了ByteBuf,它相对于ByteBuffer来说,带来了很 ...
- SSH本地端口转发的理解
ssh -L 3307:127.0.0.1:3306 user@ssh-server -N 其中127.0.0.1:3306是指 ssh-server要访问资源的ip和端口 而3307则是隧道的开口, ...
- 解决spring多线程不共享事务的问题
在一个事务中使用多线程操作数据库时,若同时存在对数据库的读写操作,可能出现数据读取的不准确,因为多线程将不会共享同一个事务(也就是说子线程和主线程的事务不一样),为了解决这个问题,可以使用spring ...
- 二、core abp 数据库迁移
一.数据库迁移-ABP(库) 1.配置链接数据库: 贴以下代码: { "ConnectionStrings": { "Default": "Serv ...
- Linux下 rewrite_mod 的配置
以下使用最新的 Ubuntu 16.04 测试; 安装好apache后先确认有没有rewrite模块,大多数情况下是有的:ls /etc/apache2/mods-available |grep re ...
- How to goproxy
brew install python python "/users/cuthead/desktop/phuslu-goproxy-9087f35/uploader.py" sel ...
- Nginx 滑动窗口与缓冲区
L:125
- Base64 总结
Base64编码是解决一些无法打印的字符无法显示的问题,将8位的ascii编码转换为6位的表示64个可见字符的算法. 具体而言,首先将编码每三个分成一组,将三个字符转换为总长为24位的二进制 数字,将 ...
- CodeForces 589F-Gourmet and Banquet-二分答案
有m盘菜,每盘有一个开始时间和结束时间,必须每盘都吃同样的时间.问最多能吃多久. 二分答案,然后用一个优先队列维护当前时间内的菜,然后每次都吃结束时间最小的那盘. #include <cstdi ...