http://codeforces.com/contest/1062/problem/E
这题两个难点,第一个是如何快速找到一堆不一定在一起的节点的公共LCA,第二个是如何确定这一堆节点中需要剔除哪一个。
可以先想一下,设原集合的lca为p1,可以取得最优答案的点为x,其他点为vi,那么x一定不在lca(所有的vi)这个点的子树上。这时候,任意一个vi和x的lca都是p1。这时假如维护一个S[l,r]表示[l,r]的公共LCA,根据上面说的性质,这个S序列一定存在一个断点,从这个点开始所有的S==p1,这样的话就可以利用二分查找找到这个断点,可以确定这个断点一定是候选答案之一。另外一个在二分的过程中其实是所有的lca都是基于最左边那个点积累过来的,所以除了分界点之外另外一个可能影响到答案的就是最左边这个点。到这里就算是找到了唯二可能影响答案的点,只需要对他们分别验证就可以了。从dalao的代码里学到了lca也可以作为线段树维护的变量。。。
贴上dalao的代码,我写的太丑了
#include <bits/stdc++.h>
#define pb push_back
#define lc (id << 1)
#define rc (id << 1 ^ 1)
#define md (l + r >> 1)
using namespace std;
const int N = 1e5 + 10, LG = 18;
int n, q, S[N * 4], H[N], P[N][LG];
vector<int> Adj[N];
void DFS(int v)
{
for (int i = 1; i < LG; i++)
P[v][i] = P[P[v][i - 1]][i - 1];
for (int &u : Adj[v])
H[u] = H[v] + 1, DFS(u);
}
inline int LCA(int v, int u)
{
if (H[v] < H[u])
return (LCA(u, v));
for (int i = 0; i < LG; i++)
if ((H[v] - H[u]) & (1 << i))
v = P[v][i];
if (v == u)
return (v);
for (int i = LG - 1; ~i; i--)
if (P[v][i] != P[u][i])
v = P[v][i], u = P[u][i];
return (P[v][0]);
}
void Build(int id = 1, int l = 1, int r = n + 1)
{
if (r - l < 2)
{
S[id] = l;
return;
}
Build(lc, l, md);
Build(rc, md, r);
S[id] = LCA(S[lc], S[rc]);
}
int Get(int le, int ri, int id = 1, int l = 1, int r = n + 1)
{
if (le <= l && r <= ri)
return (S[id]);
if (ri <= md)
return (Get(le, ri, lc, l, md));
if (md <= le)
return (Get(le, ri, rc, md, r));
return (LCA(Get(le, ri, lc, l, md), Get(le, ri, rc, md, r)));
}
int main()
{
scanf("%d%d", &n, &q);
for (int i = 2; i <= n; i++)
scanf("%d", &P[i][0]), Adj[P[i][0]].pb(i);
DFS(1);
Build();
for (; q; q--)
{
int l, r, lca;
scanf("%d%d", &l, &r);
lca = Get(l, r + 1);
int le = l, ri = r, mid;
while (ri - le > 1)
{
mid = (le + ri) >> 1;
if (Get(l, mid + 1) == lca)
ri = mid;
else
le = mid;
}
int lca1 = Get(l + 1, r + 1);
int lca2;
if (ri != r)
lca2 = LCA(Get(l, ri), Get(ri + 1, r + 1));
else
lca2 = Get(l, ri);
if (H[lca1] < H[lca2])
printf("%d %d\n", ri, H[lca2]);
else
printf("%d %d\n", l, H[lca1]);
}
return (0);
}
另一种思路是官方题解的那种,利用dfs的时间戳:在[l,r]中找到最大和最小的时间戳,这俩节点就是决定lca的点,具体证明在官方tutorial里有,不过其实直觉上挺好理解的。。按照dfs序画画就明白了。
底下这个巨丑的代码就是我写的
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+15;
const int INF=0x3f3f3f3f;
const int mod=1e9+7;
typedef long long LL;
vector<int> g[maxn];
int fa[maxn][18],stamp[maxn],ct,dep[maxn];
void dfs(int u,int p)
{
stamp[u]=++ct;
int len=g[u].size();
for(int i=0;i<len;i++)
{
if(g[u][i]==p)continue;
fa[g[u][i]][0]=u;dep[g[u][i]]=dep[u]+1;
dfs(g[u][i],u);
}
}
struct node
{
int v,id;
node(){v=id=INF;}
node(int v,int id):v(v),id(id){}
bool operator<(const struct node &rhs)const
{
return v<rhs.v;
}
};
node minx[maxn<<2],maxx[maxn<<2];
void build(int o,int l,int r)
{
if(l==r)
{
minx[o].v=stamp[l],minx[o].id=l;
maxx[o].v=stamp[l],maxx[o].id=l;
}
else
{
int m=(l+r)>>1;
build(o<<1,l,m);build(o<<1|1,m+1,r);
minx[o]=min(minx[o<<1],minx[o<<1|1]),maxx[o]=max(maxx[o<<1],maxx[o<<1|1]);
}
}
int minxid,maxxid;
int ql,qr;
node query1(int o,int l,int r)
{
if(ql<=l&&r<=qr)
return minx[o];
else
{
int m=(l+r)>>1;
node tmp;
if(ql<=m)
tmp=query1(o<<1,l,m);
if(m<qr)
tmp=min(tmp,query1(o<<1|1,m+1,r));
return tmp;
}
}
node query2(int o,int l,int r)
{
if(ql<=l&&r<=qr)
return maxx[o];
else
{
int m=(l+r)>>1;
node tmp(0,0);
if(ql<=m)
tmp=query2(o<<1,l,m);
if(m<qr)
tmp=max(tmp,query2(o<<1|1,m+1,r));
return tmp;
}
}
void initLca(int n)
{
for(int i=1;i<=17;i++)
for(int j=1;j<=n;j++)
fa[j][i]=fa[fa[j][i-1]][i-1];
}
int lca(int x,int y)
{
if(dep[x]>dep[y])swap(x,y);
int d=dep[y]-dep[x];
for(int i=0;(1<<i)<=d;i++)
if((1<<i)&d)y=fa[y][i];
if(x==y)return x;
else
{
for(int i=17;i>=0;i--)
if(fa[x][i]!=fa[y][i])
x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
int n,q,u,v;
cin>>n>>q;
for(int i=2;i<=n;i++)
{
cin>>v;
g[i].push_back(v),g[v].push_back(i);
}
dep[1]=0;
dfs(1,0);
initLca(n);
build(1,1,n);
node t1,t2;
for(int i=1;i<=q;i++)
{
cin>>ql>>qr;
t1=query1(1,1,n);t2=query2(1,1,n);
int temp=0,lca1,lca2,alca,tql=ql,tqr=qr,tans;
u=t1.id,v=t2.id;
qr=u-1;
if(qr<ql)
lca1=qr+2;
else
{
t1=query1(1,1,n);t2=query2(1,1,n);
lca1=lca(t1.id,t2.id);
}
ql=u+1,qr=tqr;
if(qr<ql)
{
lca2=ql-2;
}
else
{
t1=query1(1,1,n);t2=query2(1,1,n);
lca2=lca(t1.id,t2.id);
}
tans=lca(lca1,lca2);
ql=tql,qr=v-1;
if(qr<ql)
lca1=qr+2;
else
{
t1=query1(1,1,n);t2=query2(1,1,n);
lca1=lca(t1.id,t2.id);
}
ql=v+1,qr=tqr;
if(qr<ql)
lca2=ql-2;
else
{
t1=query1(1,1,n);t2=query2(1,1,n);
lca2=lca(t1.id,t2.id);
}
alca=lca(lca1,lca2);
if(dep[alca]>dep[tans])
cout<<v<<" "<<dep[alca]<<"\n";
else
cout<<u<<" "<<dep[tans]<<"\n";
}
return 0;
}
