P11364 [NOIP2024] 树上查询
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 LOCAL
freopen("in.txt","r",stdin);
freopen("my.out","w",stdout);
#endif
wing_heart :: main();
}
本文来自博客园,作者:wing_heart,转载请注明原文链接:https://www.cnblogs.com/wingheart/p/18701249

浙公网安备 33010602011771号