LGP11364 [NOIP 2024] 树上查询 学习笔记
LGP11364 [NOIP 2024] 树上查询 学习笔记
前言
过了八个月后……
\(\text{Finally, done.}\)
题意简述
给定一棵 \(n\) 个结点、以 \(1\) 为根的树。定义一个结点 \(u\) 的深度为 \(1\to u\) 简单路径上的结点数量。
定义 \(\text{LCA*}(l,r)\) 为编号在 \([l,r]\) 中的所有结点的最近公共祖先。
\(m\) 个询问,每个询问给出 \(l,r,k\),你需要回答:\(\max_{l\le l'\le r'\le r\land r'-l'+1\ge k}dep_{\text{LCA*}(l',r')}\)。
\(n,m\le 5\times 10^5\)。对于每个询问保证 \(r'-l'+1\ge k\)。虽然两 \(\log\) 有不小机会干过去,但那设计上只有 \(\text{80pts}\)。为了达到训练效果,请写单 \(\log\) 做法。
做法解析
首先这个问题需要一个转化。很大程度上,原题这个纸糊的,看起来很难的描述“\(\text{LCA*}(l,r)\)”就是导致很多选手没拿到高分的原因。
但是呢,你需要一点点结论,然后就行了。
首先一个结论是:\(\text{LCA*}(l,r)\) 即为 \(\text{lca}(i,i+1),i\le [l,r)\) 中最浅者。为什么呢?首先显然 \(\text{LCA*}(l,r)\) 肯定实在在所有 \(\text{lca}(i,i+1)\) 往上走的直链的交上的最深位置。然后这个位置 \(p\) 如果不是任何 \(\text{lca}(i,i+1)\),那就必然有所有 \(i\le [l,r]\) 都在 \(p\) 的同一棵子树里,那显然 \(p\) 就不会是最近的公共祖先。
那上述的结论说明什么呢?因为所有的 \(\text{LCA*}(l,r)\) 都属于 \(\text{lca}(i,i+1)\),而后者的数量恰有 \(n-1\) 个,我们可以直接把它们求出来。
并且,\(\text{lca}\) 随着点集合并而合并的过程类似于取 \(\max\),所以其管辖区间形态类似于笛卡尔树。我们把这棵笛卡尔树建出来,求出 \(n-1\) 个 \([sl,sr,v]\),意为对于 \([sl,sr]\) 区间内的任意点集,其深度必然不小于 \(v\)。特别地,\(k=1\) 时 \(\text{lca}\) 实际上就是单点,所以我们还要加入所有的 \([i,i,\text{dep}_i]\)。
问题转化完毕。有 \(O(n)\) 个形如 \([sl,sr]\),权值为 \(v\) 的线段,\(m\) 次询问,每次给定 \([ql,qr,k]\),问与区间 \([ql,qr]\) 交集至少为 \(k\) 的线段中最大的权值。
首先显然可以三维偏序来做,此不赘述。现在你有 \(\text{80pts}\) 了。但是我们来思考优美的单 \(\log\)。
想要单 \(\log\) 需要偏序关系降到二维。怎么降呢?答案是分类讨论。
对于 \(sl\le ql\le qr\le sr\) 的,我们不需要考虑 \(k\) 这一维的偏序,因为题目保证了 \(qr-ql+1\ge k\),所以也必然有 \(sr-sl+1\ge k\)。这里就是直接二维偏序就行。
对于 \(ql\le sl\le qr\le sr\) 或 \(sl\le ql\le sr\le qr\) 的,我们去扫描 \(k\) 这一维,这个想法较为重要,值得学习:
- 一方面我们发现这两个问题有对称性,而 \(k\) 是它们都免不掉要扫的一维。
- 另一方面因为我们需要降维,我们必须就要松掉一维度的限制。我们对于 \(ql\le sl\le qr\le sr\) 松掉 \(qr\le sr\) 这一维,对于 \(sl\le ql\le sr\le qr\) 松掉 \(sl\le ql\) 这一维。这样我们既降了维,又允许出现 \(ql\le sl\le sr\le qr\) 的情况,顺便就把它统计上了。
这时如果你饭堂了,可能会问出这样一个问题:
那 \(ql\le sl\le sr\le qr\) 的情况不是明晃晃算重了吗?
的确,这种情况被考虑了两遍,但我们在做的是更新最大值而并不是加和,所以最大值你重复扫对答案正确性没有影响。
推而广之来讲,求最大值的时候,你更新失败是在 maxxer(ans,0),就对答案正确性没有影响。
代码实现
纯板,代码没有任何好讲的。
#include <bits/stdc++.h>
using namespace std;
using namespace obasic;
const int MaxN=5e5+5;
int N,M,X,Y,Z,ans[MaxN];
vector<int> Tr[MaxN];
void addudge(int u,int v){
Tr[u].push_back(v);
Tr[v].push_back(u);
}
int tfa[MaxN],dep[MaxN],siz[MaxN],hvs[MaxN];
void dfs1(int u,int f){
dep[u]=dep[f]+1,tfa[u]=f,siz[u]=1;
for(int v : Tr[u]){
if(v==f)continue;
dfs1(v,u),siz[u]+=siz[v];
if(siz[v]>siz[hvs[u]])hvs[u]=v;
}
}
int dfn[MaxN],dcnt,top[MaxN];
void dfs2(int u,int t){
dfn[u]=++dcnt,top[u]=t;
if(hvs[u])dfs2(hvs[u],t);
for(int v : Tr[u])if(v!=tfa[u]&&v!=hvs[u])dfs2(v,v);
}
int getlca(int x,int y){
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]])swap(x,y);
x=tfa[top[x]];
}
return dep[x]<=dep[y]?x:y;
}
struct SegTree{
int mx[MaxN<<2];
int ls(int u){return u<<1;}
int rs(int u){return (u<<1)|1;}
void pushup(int u){mx[u]=max(mx[ls(u)],mx[rs(u)]);}
void update(int u,int cl,int cr,int dd,int x){
if(cl==cr){maxxer(mx[u],x);return;}int cmid=(cl+cr)>>1;
dd<=cmid?update(ls(u),cl,cmid,dd,x):update(rs(u),cmid+1,cr,dd,x);pushup(u);
}
int getmax(int u,int cl,int cr,int dl,int dr){
if(dl<=cl&&cr<=dr)return mx[u];
int cmid=(cl+cr)>>1,res=0;
if(dl<=cmid)maxxer(res,getmax(ls(u),cl,cmid,dl,dr));
if(dr>cmid)maxxer(res,getmax(rs(u),cmid+1,cr,dl,dr));
return res;
}
}SgT[3];
int A[MaxN],H[MaxN],stk[MaxN],ktp;
int sls[MaxN],srs[MaxN];
struct anob{int l,r,x;}S[MaxN];
vector<pii> G2[MaxN],Q2[MaxN];
vector<anob> G1[MaxN],Q1[MaxN];
void dfs3(int u,int l,int r){
S[u]={l,r,H[u]};
if(sls[u])dfs3(sls[u],l,u);
if(srs[u])dfs3(srs[u],u+1,r);
}
int main(){
readi(N);
for(int i=1;i<N;i++)readis(X,Y),addudge(X,Y);
dfs1(1,0),dfs2(1,1);for(int i=1;i<N;i++)A[i]=getlca(i,i+1),H[i]=dep[A[i]];
for(int i=1,k;i<N;i++,ktp=k){
for(k=ktp;H[stk[k]]>H[i];k--);
if(k<ktp)sls[i]=stk[k+1];
srs[stk[k]]=i,stk[++k]=i;
}
dfs3(stk[1],1,N);
readi(M);
for(int i=1;i<=M;i++){
readis(X,Y,Z);
Q1[Z].push_back({X,Y,i});
Q2[X].push_back({Y,i});
}
for(int i=1;i<N;i++){
auto [l,r,h]=S[i];
G1[r-l+1].push_back(S[i]);
}
for(int i=1;i<=N;i++)G1[1].push_back({i,i,dep[i]});
for(int k=N;k>=1;k--){
for(auto [l,r,h] : G1[k]){
SgT[0].update(1,1,N,l,h);
SgT[1].update(1,1,N,r,h);
}
for(auto [l,r,id] : Q1[k]){
maxxer(ans[id],SgT[0].getmax(1,1,N,l,r-k+1));
maxxer(ans[id],SgT[1].getmax(1,1,N,l+k-1,r));
}
}
for(int i=1;i<N;i++){
auto [l,r,h]=S[i];
G2[l].push_back({r,h});
}
for(int i=1;i<=N;i++){
for(auto [r,h] : G2[i])SgT[2].update(1,1,N,r,h);
for(auto [r,id] : Q2[i])maxxer(ans[id],SgT[2].getmax(1,1,N,r,N));
}
for(int i=1;i<=M;i++)writil(ans[i]);
return 0;
}
浙公网安备 33010602011771号