首先,我们从 u -> v 有一个明显的贪心,即能向上跳的时候尽量向深度最浅的节点跳。这个我们可以用树上倍增来维护。我们可以认为 u 贪心向上跳后不超过 lca 能跳到 u' 的位置, v 跳到 v' 的位置,这时只需要查询一下是否有 u' -> v' 的直达公交线路就可以确定出答案了。

  如果 u 和 v 在一条链上,我们可以直接倍增获得答案,所以以下讨论均为 u != lca && v != lca 的情况。 若在节点 u 和 v 之间存在一条直达线路,说明有一条线路的起点在以 u 为根的子树内,重点在以 v 为根的子树内。即起点的dfs序 >= dfn[u] && <= dfn[u] + size[u] - 1, 终点的 dfs序 >= dfn[v] && <= dfn[v] + size[v] -1;这是一个二维限制的问题,暴力主席树就可以搞定……

  之后看题解发现其实有一种妙妙的做法并不需要主席树。我们可以离线处理。要查询是否有起点在 u 的子树内且终点在 v 的子树内的路线,就是dfs在进入 u 的子树内之前记录下 v 的子树内此时记录的路径数,然后进入子树,遇到路线则标记终点所在的位置。返回之后两相对比,如果此时 v 的子树内记录的路径数有增长的话说明这样的路线是存在的。

#include <bits/stdc++.h>
using namespace std;
#define maxn 450000
#define INF 99999999
#define CNST 20
int n, m, q, Mul[maxn][CNST], gra[maxn][CNST];
int dfn[maxn], dep[maxn], bits[];
int tot, timer, root[maxn], fa[maxn], size[maxn]; int read()
{
int x = , k = ;
char c; c = getchar();
while(c < '' || c > '') { if(c == '-') k = -; c = getchar(); }
while(c >= '' && c <= '') x = x * + c - '', c = getchar();
return x * k;
} struct edge
{
int cnp, to[maxn], last[maxn], head[maxn];
edge() { cnp = ; }
void add(int u, int v)
{ to[cnp] = v, last[cnp] = head[u], head[u] = cnp ++; }
}E1, G; struct edge1
{
int u, v;
edge1(int _u = , int _v = ) { u = _u, v = _v; }
friend bool operator <(const edge1& a, const edge1& b)
{ return (dfn[a.u] != dfn[b.u]) ? dfn[a.u] < dfn[b.u] : dfn[a.v] < dfn[b.v]; }
}e[maxn]; struct node
{
int u, num;
node(int _u = , int _num = )
{ u = _u, num = _num; }
}; struct node1
{
int sum, ls, rs;
}T[maxn * ]; void dfs(int u)
{
gra[u][] = fa[u], dfn[u] = ++ timer; size[u] = ;
for(int i = ; i < CNST; i ++) gra[u][i] = gra[gra[u][i - ]][i - ];
for(int i = E1.head[u]; i; i = E1.last[i])
{
int v = E1.to[i];
dep[v] = dep[u] + ; dfs(v);
size[u] += size[v];
}
} int dfs2(int u)
{
int lim = ;
for(int i = G.head[u]; i; i = G.last[i])
{
if(u != G.to[i]) lim = (dep[lim] < dep[G.to[i]]) ? lim : G.to[i];
if(!lim && u != G.to[i]) lim = G.to[i];
}
for(int i = E1.head[u]; i; i = E1.last[i])
{
int v = E1.to[i];
int t = dfs2(v);
if(u != t && t) lim = (dep[lim] < dep[t]) ? lim : t;
if(!lim && u != t) lim = t;
}
Mul[u][] = lim;
return lim;
} void dfs3(int u)
{
for(int i = ; i < CNST; i ++)
Mul[u][i] = Mul[Mul[u][i - ]][i - ];
for(int i = E1.head[u]; i; i = E1.last[i])
dfs3(E1.to[i]);
} int LCA(int u, int v)
{
if(dep[u] < dep[v]) swap(u, v);
for(int i = CNST - ; ~i; i --)
if(dep[gra[u][i]] >= dep[v]) u = gra[u][i];
for(int i = CNST - ; ~i; i --)
if(gra[u][i] != gra[v][i]) u = gra[u][i], v = gra[v][i];
return (u == v) ? u : gra[u][];
} node Jump(int u, int fu)
{
bool flag = ; int cnt = ;
if(dep[u] == dep[fu]) flag = ;
for(int i = CNST - ; ~i; i --)
{
if(!Mul[u][i]) continue;
if(dep[Mul[u][i]] <= dep[fu]) flag = ;
if(dep[Mul[u][i]] > dep[fu]) u = Mul[u][i], cnt += bits[i];
}
if(!flag) return node(-, );
else return node(u, cnt);
} void Ins(int &now, int pre, int l, int r, int x)
{
now = ++ tot; T[now] = T[pre]; T[now].sum ++;
if(l == r) return;
int mid = (l + r) >> ;
if(x <= mid) Ins(T[now].ls, T[pre].ls, l, mid, x);
else Ins(T[now].rs, T[pre].rs, mid + , r, x);
} bool Query(int now, int pre, int l, int r, int L, int R)
{
if(!now) return ;
if(L == l && R == r) return (T[now].sum - T[pre].sum);
if(L > r || R < l) return ;
int mid = (l + r) >> ;
if(R <= mid) return Query(T[now].ls, T[pre].ls, l, mid, L, R);
else if(L > mid) return Query(T[now].rs, T[pre].rs, mid + , r, L, R);
else
{
if(Query(T[now].ls, T[pre].ls, l, mid, L, mid)) return ;
else return Query(T[now].rs, T[pre].rs, mid + , r, mid + , R);
}
} int main()
{
n = read();
bits[] = ; for(int i = ; i < CNST; i ++) bits[i] = bits[i - ] << ;
for(int i = ; i <= n; i ++)
{
fa[i] = read();
E1.add(fa[i], i);
}
dep[] = ; dfs();
m = read();
for(int i = ; i <= m; i ++)
{
int u = read(), v = read(), lca = LCA(u, v);
G.add(u, lca); G.add(v, lca);
e[i] = edge1(u, v);
}
dfs2(); dfs3();
sort(e + , e + + m); int last = ; root[] = tot = ;
for(int i = ; i <= m; i ++)
{
while(last < dfn[e[i].u] - ) T[root[last + ] = ++ tot] = T[root[last]], last ++;
Ins(root[dfn[e[i].u]], root[last], , n, dfn[e[i].v]);
if(last != dfn[e[i].u]) last ++;
}
while(last + <= n) T[root[last + ] = ++ tot] = T[root[last]], last ++;
q = read();
for(int i = ; i <= q; i ++)
{
int u = read(), v = read(), lca = LCA(u, v);
node a = Jump(u, lca), b = Jump(v, lca);
int fu = a.u, fv = b.u;
if(fu == - || fv == -) { printf("-1\n"); continue; }
if(u == lca || v == lca) { printf("%d\n", a.num + b.num + ); continue; }
int l1 = dfn[fu], r1 = dfn[fu] + size[fu] - ;
int l2 = dfn[fv], r2 = dfn[fv] + size[fv] - ;
int x = Query(root[r1], root[l1 - ], , n, l2, r2);
int y = Query(root[r2], root[l2 - ], , n, l1, r1); printf("%d\n", a.num + b.num + + !(x || y));
}
return ;
}

【题解】CF#983 E-NN country的更多相关文章

  1. 【CodeForces】983 E. NN country 树上倍增+二维数点

    [题目]E. NN country [题意]给定n个点的树和m条链,q次询问一条链(a,b)最少被多少条给定的链覆盖.\(n,m,q \leq 2*10^5\). [算法]树上倍增+二维数点(树状数组 ...

  2. 【codeforces 983E】NN country

    Description In the NN country, there are n cities, numbered from 1 to n, and n−1 roads, connecting t ...

  3. 竞赛题解 - CF Round #524 Div.2

    CF Round #524 Div.2 - 竞赛题解 不容易CF有一场下午的比赛,开心的和一个神犇一起报了名 被虐爆--前两题水过去,第三题卡了好久,第四题毫无头绪QwQ Codeforces 传送门 ...

  4. 题解——CF Manthan, Codefest 18 (rated, Div. 1 + Div. 2) T5(思维)

    还是dfs? 好像自己写的有锅 过不去 看了题解修改了才过qwq #include <cstdio> #include <algorithm> #include <cst ...

  5. 竞赛题解 - [CF 1080D]Olya and magical square

    Olya and magical square - 竞赛题解 借鉴了一下神犇tly的博客QwQ(还是打一下广告) 终于弄懂了 Codeforces 传送门 『题目』(直接上翻译了) 给一个边长为 \( ...

  6. Codeforces983E. NN country

    新鲜出炉! $n \leq 200000$的树,给$m \leq 200000$条链,$q \leq 200000$个询问,每次问一条询问链最少用m条中的几条给定链覆盖其所有边,可能无解. 首先确定一 ...

  7. CF983E NN country(倍增,差分)

    题意 给定一棵树和若干条路线,每条路线相当于树上 x,y 之间的路径,途径路径上的每个点 给出若干个询问,每次询问从 u 到 v 至少需要利用几条路线 N,M,Q≤200000 题解 构建倍增数组g[ ...

  8. [题解] [CF 1250J] The Parade

    题面 题目大意: 给定一个 \(n\) , 所有军人的数量均在 \([1, n]\) 给定 \(a_i\) 代表高度为 \(i\) 的军人的个数 你要将这些军人分成 \(k\) 行, 满足下面两个条件 ...

  9. 题解 CF 1372 B

    题目 传送门 题意 给出 \(n\),输出 \(a\) ,\(b\) (\(0 < a \leq b < n\)),使\(a+b=n\)且 \(\operatorname{lcm}(a,b ...

随机推荐

  1. JavaSE基础笔记

    JVM 不是跨平台的,他是用 C++编写的. Path 环境变量的地址是 ...jdk/bin java_home 地址是 ...jdk

  2. leetcode笔记--7 Find the Difference

    question: Given two strings s and t which consist of only lowercase letters. String t is generated b ...

  3. 「日常训练&知识学习」树的分块(王室联邦,HYSBZ-1086)

    题意与分析 这题的题意就是树分块,更具体的看题目(中文题). 学习这一题是为了树的分块,为树上莫队做铺垫. 参考1:https://blog.csdn.net/LJH_KOQI/article/det ...

  4. ionic typescript--验证码发送倒计时功能

    1.新建页面 ionic g page forget   2.mode.html文件 <ion-item> <ion-input clearInput [(ngModel)]='co ...

  5. 完全背包问题 :背包dp

    题目描述: 有 N种物品和一个容量是 V 的背包,每种物品都有无限件可用.第 i 种物品的体积是 vi,价值是 wi. 求解将哪些物品装入背包,可使这些物品的总体积不超过背包容量,且总价值最大.输出最 ...

  6. POJ 2104 K-th Number(划分树)

    Description You are working for Macrohard company in data structures department. After failing your ...

  7. Jamie and Alarm Snooze

    Description Jamie loves sleeping. One day, he decides that he needs to wake up at exactly hh: mm. Ho ...

  8. 软工第三次作业——个人PSP

    9.22--9.26本周例行报告 1.PSP(personal software process )个人软件过程. 类型 任务 预计时间 开始时间 结束时间 中断时间 实际用时 准备工作 学习重定向 ...

  9. AndroidUI设计之 布局管理器 - 详细解析布局实现

    写完博客的总结 : 以前没有弄清楚的概念清晰化 父容器与本容器属性 : android_layout...属性是本容器的属性, 定义在这个布局管理器的LayoutParams内部类中, 每个布局管理器 ...

  10. 3dContactPointAnnotationTool开发日志(二十)

      为了使工具更人性化,我又在每个status的text上绑了个可以拖拽实现值改变的脚本,但是不知道为啥rotx那个值越过+-90范围后连续修改就会产生抖动的现象,试了很多方法也没能弄好,不过实际用起 ...