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\)。刻画一下合法状态:

\[y_i-x_i+1 \ge k \land \begin{cases} y_i \le r \land y_i - l +1 \ge k & (y_i \le r)\\ y_i > r \land r-x_i+1 \ge k & (y_i > r) \end{cases} \]

这是三维偏序吗?不是的。可以化简成:

\[\begin{cases} k+l-1 \le y_i \le r \land y_i-x_i \ge k-1 & (1)\\ y_i>r \land x_i \le r-k+1 & (2) \end{cases} \]

式子 \(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();
}
posted @ 2025-02-06 21:06  wing_heart  阅读(82)  评论(0)    收藏  举报