题目
思路
确实是数据结构学傻了!
数据结构硬上
考虑一个 ,至少有两个点分属于不同的儿子子树。如果它只有两个儿子子树,并且其中一个内部只有一个点,则删去之可以使 更低。即:
右边的“唯一点”删去之后是有点作用的。而左边的多个点无论删去哪个都没用。特判掉两个点(两个子树内都是只有一个点)的情况。多个子树时,无用。 本身是一个被选中的点,则必删。
感觉并不是很好直接使用线段树,因为 变来变去。那就 把询问离线,这样便于我们甩掉一个维度。莫队?算了。还是按照右端点排序。
利用 动态 的思想,我们树链剖分,轻儿子暴力改,重儿子统一改。现在我们要维护的无非就是编号不小于 的所有点,属于某个点的哪些子树。
根据以往的套路,对于每颗子树存 编号最大的一个点,以此检测区间。这样一来,对于一个 ,若有至少三个儿子的“代表点”是不小于 的,删谁都无所谓,可以输出答案。否则,我们可以试着删去其中一个子树的其中一个点,因为只有两颗子树了。
所以,对于一个点,将它的轻儿子用可删堆维护最大的 个。当我们的询问右端点从 变成 时,将 到根路径上的所有点都修改一下,重链上的直接整体赋值,链顶由于是一个轻儿子,将其父亲修改。
复杂度是
的。其实应该还好,因为可删堆里面的元素应该不会很多。如果会,当我没说。 不过树剖的整体赋值似乎也是这个复杂度。
学傻了的我
看看题解,学到一个冷知识,只需要删除 最小或最大的点。
然后我辛辛苦苦地打了 表维护 ,用的 以求做到线性,故 。
还是有点常数的。代码将会在下方展示。
优美的解法
去证明上面那个冷知识即可。那就是,很多点的 就是这些点中 最小和最大的两个点的 。
怎么证明?可以用“子树内的 连续”证明。记 最小和最大的两个点分别为 ,显然 子树的 区间是包含 。于是 在二者之间的一定也在 子树中。于是 铁定不变。
事实上,这早就在讲“虚树”时证明过了。
既然如此,用什么 表啊?直接找到 最小和最大的两个点,求一个 ,不香吗?
代码
#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;
typedef long long int_;
inline int readint(){
int a = 0; char c = getchar(), f = 1;
for(; c<'0'||c>'9'; c=getchar())
if(c == '-') f = -f;
for(; '0'<=c&&c<='9'; c=getchar())
a = (a<<3)+(a<<1)+(c^48);
return a*f;
}
const int MaxN = 100005;
int n, m;
struct Edge{
int to, nxt; Edge(){}
Edge(int T,int N){
to = T, nxt = N;
}
};
Edge e[MaxN<<1];
int cntEdge, head[MaxN];
void clear_graph(){
for(int i=1; i<=n; ++i)
head[i] = -1;
cntEdge = 0;
}
void addEdge(int a,int b){
e[cntEdge] = Edge(b,head[a]);
head[a] = cntEdge ++;
}
int dep[MaxN]; // 每个点的深度
int fa[MaxN][20]; // 倍增数组
int dfn[MaxN], dfsClock; // dfs序
void build(int x,int pre = 0){
dfn[x] = ++ dfsClock;
dep[x] = dep[pre]+1;
fa[x][0] = pre; // 简化递归过程
for(int j=0; j+1<20; ++j){
if(fa[x][j] == 0) break;
fa[x][j+1] = fa[fa[x][j]][j];
}
for(int i=head[x]; ~i; i=e[i].nxt)
if(e[i].to != pre)
build(e[i].to,x);
}
int getLca(int a,int b){
if(!a || !b) return a+b;
if(dep[a] < dep[b]) swap(a,b);
for(int j=19; ~j; --j)
if((dep[a]-dep[b])>>j&1)
a = fa[a][j];
if(a == b) return a;
for(int j=19; ~j; --j)
if(fa[a][j] != fa[b][j])
a = fa[a][j], b = fa[b][j];
return fa[a][0];
}
namespace UFS{
int fa[MaxN], rnk[MaxN];
int val[MaxN]; // “根”节点
void init(){
for(int i=1; i<=n; ++i){
fa[i] = val[i] = i;
rnk[i] = 1; // itself
}
}
inline int find(int a){
if(a != fa[a])
fa[a] = find(fa[a]);
return fa[a];
}
void combine(int a,int b){
a = find(a), b = find(b);
if(rnk[a] > rnk[b]) swap(a,b);
if(dep[val[a]] < dep[val[b]])
val[b] = val[a]; // 最浅点
fa[a] = b, rnk[b] += rnk[a];
}
}
vector< int > q[MaxN];
bool vis[MaxN]; // 是否已经访问
void tarjan(int x,int pre = 0){
for(int i=head[x]; ~i; i=e[i].nxt)
if(e[i].to != pre){
tarjan(e[i].to,x);
UFS::combine(e[i].to,x);
}
vis[x] = true; // 防止 祖先-后代
int len = q[x].size();
for(int i=0,y; i<len; ++i)
if(vis[y = q[x][i]]){
y = UFS::find(y);
q[x][i] = UFS::val[y];
}
else q[x][i] = -1;
}
int lca[20][MaxN], lg2[MaxN];
int st1[20][MaxN]; // 最小 dfn
int st2[20][MaxN]; // 最大 dfn
void prepare(){
lg2[1] = 0; // 2^0 = 1
for(int i=2; i<=n; ++i)
lg2[i] = lg2[i>>1]+1;
for(int i=1; i<=n; ++i){
st1[0][i] = st2[0][i] = i;
lca[0][i] = i; // itself
}
for(int j=1; (1<<j)<=n; ++j){
int len = (1<<j>>1);
for(int i=1; i+len*2-1<=n; ++i){
int a = lca[j-1][i];
int b = lca[j-1][i+len];
q[a].push_back(b);
q[b].push_back(a);
a = st1[j-1][i];
b = st1[j-1][i+len];
if(dfn[a] < dfn[b])
st1[j][i] = a;
else st1[j][i] = b;
a = st2[j-1][i];
b = st2[j-1][i+len];
if(dfn[a] > dfn[b])
st2[j][i] = a;
else st2[j][i] = b;
}
for(int i=1; i<=n; ++i)
vis[i] = false;
UFS::init(); tarjan(1);
for(int i=n-len*2+1; i; --i){
int a = lca[j-1][i];
int b = lca[j-1][i+len];
if(q[a].back() != -1)
lca[j][i] = q[a].back();
else lca[j][i] = q[b].back();
q[a].pop_back();
q[b].pop_back();
}
}
}
int queryLca(int l,int r){
if(l > r) return 0;
int k = lg2[r-l+1];
if((1<<k) == r-l+1)
return lca[k][l];
return getLca(lca[k][l],
lca[k][r-(1<<k)+1]);
}
pair< int,int > queryDfn(int l,int r){
pair< int,int > res;
int k = lg2[r-l+1], a, b;
a = st1[k][l];
b = st1[k][r-(1<<k)+1];
if(dfn[a] > dfn[b]) a = b;
res.first = a;
a = st2[k][l];
b = st2[k][r-(1<<k)+1];
if(dfn[a] < dfn[b]) a = b;
res.second = a;
return res;
}
int main(){
n = readint(), m = readint();
clear_graph();
for(int i=2; i<=n; ++i)
addEdge(readint(),i);
build(1), prepare();
for(int l,r; m; --m){
l = readint(), r = readint();
auto away = queryDfn(l,r);
int xjh = queryLca(l,away.first-1);
xjh = getLca(xjh,queryLca(away.first+1,r));
int xez = queryLca(l,away.second-1);
xez = getLca(xez,queryLca(away.second+1,r));
if(dep[xjh] < dep[xez])
xjh = xez, away.first = away.second;
printf("%d %d\n",away.first,dep[xjh]-1);
}
return 0;
}