P11364 [NOIP2024] 树上查询
题意
给你一棵 \(n\) 个结点,以 \(1\) 为根的树。定义 \(\text{LCA}^*(l,r)\) 表示编号在 \([l,r]\) 间的所有的结点的最近公共祖先。有 \(q\) 次询问,每次给出 \(l,r\),问满足 \([l',r'] \subseteq [l,r]\) 且区间长度至少为 \(k\) 的最大的 \(\text{dep}_{\text{LCA}^*(l',r')}\) 是多少。
\(n,q \le 5 \times 10^5\)。
思路
不难发现 \(\text{LCA}^*(l,r)\) 就是 \(\min_{i=l}^{r-1} \text{dep}_{\text{lca}_{i,i+1}}\),特判 \(l=r\) 的情况。可以根据欧拉序求 \(\text{LCA}\) 或者随便理解。
于是我们有了朴素的 \(O(qn^2)\) 暴力:预处理相邻 \(\text{LCA}\) 的深度,每次询问枚举合法的 \(l',r'\) 然后 st 表做。
设 \(d_i = dep_{lca_{i,i+1}}\),考虑把 \(d_i\) 建笛卡尔树,以便分析性质做一些预处理。
每次找出最小的 \(d_i\) 做根。我们要尽量大的答案。一个 \(d_i\) 可以做答案当且仅当 \([l,r]\) 可以选出一个长度至少为 \(k\) 的在子树内的区间,就是和子树表示的区间的交至少长度是 \(k\)。但是不好保证选出的区间一定覆盖了 \(d_i\)?
每次找出最大的 \(d_i\) 做根。预处理出每个 \(d_i\) 向左右扩展出的极大区间 \([x_i,y_i]\) 满足 \([x_i,y_i]\) 里面没有比 \(d_i\) 小的数。可以单调栈预处理。
\(d_i\) 可以做答案当且仅当 \([l,r]\) 与 \([x_i,y_i]\) 的交长度至少是 \(k\),且选出来的区间不覆盖 \(d_i\) 也不影响答案。
每次询问,找出最大的合法 \(d_i\)。刻画一下合法状态:
这是三维偏序吗?不是的。可以化简成:
式子 \(1\) 可以扫描询问,把满足 \(y_i-x_i \ge k-1\) 的点放进数据结构里,维护 \(y_i\),然后对每个询问查询 \(y_i \in [k+l-1,r]\) 的 \(d_i\) 最大的点。
式子 \(2\) 可以扫描询问,把满足 \(y_i > r\) 的点放进数据结构里,维护 \(x_i\),然后对每个询问查询 \(x_i \le r-k+1\) 的 \(d_i\) 最大的点。
时间复杂度 \(O(n \log n)\)。(\(n,q\) 同阶)
code
#include<bits/stdc++.h>
#define sf scanf
#define pf printf
#define rep(x,y,z) for(int x=y;x<=z;x++)
#define per(x,y,z) for(int x=y;x>=z;x--)
using namespace std;
typedef long long ll;
namespace wing_heart {constexpr int N=5e5+7;int n,u,v;vector<int> son[N];int fa[N],dfn[N],dep[N],dfn0;int st[30][N],stdep[30][N];int q,l,r,k;void dfs(int u,int f) {dep[u]=dep[f]+1;dfn[u]=++dfn0;st[0][dfn0]=dep[f];stdep[0][u]=dep[u];for(int v : son[u]) if(v^f) dfs(v,u);}void lca_init() {int lg=__lg(n);rep(k,1,lg) for(int i=1;i+(1<<k)-1<=n;i++) st[k][i]=min(st[k-1][i],st[k-1][i+(1<<(k-1))]), stdep[k][i]=max(stdep[k-1][i],stdep[k-1][i+(1<<(k-1))]);}int d[N];int getlca(int u,int v) {int mn=min(dfn[u],dfn[v])+1,mx=max(dfn[u],dfn[v]);int lg=__lg(mx-mn+1);return min(st[lg][mn],st[lg][mx-(1<<lg)+1]);}int stmax(int l,int r) {int lg=__lg(r-l+1);return max(stdep[lg][l],stdep[lg][r-(1<<lg)+1]);}struct node {int id,a,b,c;}x[N<<1];bool cmp1 (node x,node y) {int x1=!x.id ? x.b-x.a : x.c-1, y1=!y.id ? y.b-y.a : y.c-1; return x1==y1 ? x.id < y.id : x1>y1;}bool cmp2 (node x,node y) {return x.b==y.b ? x.id > y.id : x.b > y.b;}int t[N],top;int ans[N];struct seg {int mx[N<<2];void clear() {memset(mx,0,sizeof(mx));}void pushup(int u) { mx[u]=max(mx[u<<1],mx[u<<1|1]); }void insert(int x,int val,int u=1,int l=1,int r=n) {if(l==r) return mx[u]=val, void(0);int mid=(l+r)>>1;if(x<=mid) insert(x,val,u<<1,l,mid);else insert(x,val,u<<1|1,mid+1,r);pushup(u);}int query(int L,int R,int u=1,int l=1,int r=n) {if(l>=L && r<=R) return mx[u];int mid=(l+r)>>1;int s=0;if(L<=mid) s=query(L,R,u<<1,l,mid);if(mid+1<=R) s=max(s,query(L,R,u<<1|1,mid+1,r));return s;}}T;void main() {sf("%d",&n);rep(i,1,n-1) sf("%d%d",&u,&v), son[u].push_back(v), son[v].push_back(u);dfs(1,0);lca_init();rep(i,1,n-1) d[i]=getlca(i,i+1);rep(i,1,n-1) {x[i].c=d[i];while(top && x[t[top]].c>d[i]) x[t[top]].b=i-1, --top;t[++top]=i;}while(top) x[t[top]].b=n-1, --top;per(i,n-1,1) {while(top && x[t[top]].c>d[i]) x[t[top]].a=i+1, --top;t[++top]=i;}while(top) x[t[top]].a=1, --top; sf("%d",&q);rep(i,1,q) {sf("%d%d%d",&l,&r,&k);--r, --k;x[i+n-1]={i,l,r,k};}sort(x+1,x+n+q,cmp1);rep(i,1,n+q-1) {int id=x[i].id,a=x[i].a,b=x[i].b,c=x[i].c;if(!id) {T.insert(b,c);} else {if(!c) {ans[id]=stmax(a,b+1);continue;}ans[id]=T.query(c+a-1,b);}}sort(x+1,x+n+q,cmp2);T.clear();rep(i,1,n+q-1) {int id=x[i].id,a=x[i].a,b=x[i].b,c=x[i].c;if(!id) {T.insert(a,c);} else {if(!c) continue;ans[id]=max(ans[id],T.query(1,b-c+1));}}rep(i,1,q) pf("%d\n",ans[i]);}
}
int main() {#ifdef LOCALfreopen("in.txt","r",stdin);freopen("my.out","w",stdout);#endifwing_heart :: main();
}