[NOIP2024] 树上查询
先来考虑一个子问题
我们给定一个树,每次询问下标在一个区间的所有点的最近公共祖先的深度
定义v数组,\(v_i\)就表示第i个点和第i+1个点之间的最近公共祖先的深度,对于一个lr的询问,我们将lr-1的v值求一个最小值即为答案
这个做法为什么是对的呢,对于一个节点能够成为一个区间所有点的最近公共祖先,一定存在两个区间内的点分别存在于他的两个不同的子树内,并且区间内的所有点都存在于当前这颗子树下,所以至少有一对相邻点分别位于两个不同的子树,通过v数组一定能找到当前点
我们仔细观察一下v数组的性质
因为我们的询问是查询的最小值,所以对于一个v值,当我们查询的左右端点在一定区间内时,答案都是同一个v值,我们简称v区间,我们可以将这个区间预处理出来,显然,这个区间就是找到每个v值左右第一个大于它的v值,就是区间的左右端点
这个过程可以通过单调栈来优化到O(n)
重新考虑原题的查询
给出三个参数l,r,k,表示他想知道[l,r]中任意长度大于等于k的连续子区间的最近公共祖先深度的最大值
在先前v数组和区间的前提下,我们可以将问题转化为找到一个v区间,这个v区间满足
- 与查询区间的交区间长度>=k
- 在所有满足条件的v区间中找到v值最大的区间
暴力的解法就是遍历所有的区间,求一下交区间长度,然后在满足条件的区间内求一个v值最大
时间复杂度已经达到比较优秀的\(O(nq)\)了,可以拿到20pts的高分
优化部分
考虑我们的时间复杂度浪费在什么地方了,对于第二个条件很好求解,但是第一个条件即与查询区间的交区间长度>=k很难处理
我么可以对合法区间进行分讨,又tm出现两种情况,我们将查询区间左右端点表示为l,r,v区间左右端点表示为x,y
- 当前v区间的右端点在查询区间的右边,而v区间的左端点到查询区间的距离大于等于k,表示为
\[y>=r&&x<=r-k+1
\]
- 当前v区间的右端点都在查询区间右端点以左,查询区间左端点以右至少k的长度,同时自身长度大于等于k,表示为
\[l+k<=y<r&&y-x>=k
\]
我们将v区间都挂到右端点上,第一次对r进行扫描线,第二次对k进行扫描线,统计答案
时间复杂度\(O(qlogn)\)
ACcode
using namespace std;
#define ll long long
#define debug cout<<"flyfree\n";
#define MAXN 500010
inline ll read(){
ll x=0,f=1;
char c=getchar();
while(c<'0'||c>'9'){
if(c=='-')f=-1;
c=getchar();
}
while(c>='0'&&c<='9'){
x=x*10+c-'0';
c=getchar();
}
return x*f;
}
struct node_Sagtr{
ll l[MAXN*4],r[MAXN*4],maxz[MAXN*4];
void build(ll now,ll lz,ll rz){
l[now]=lz,r[now]=rz,maxz[now]=0;
if(lz==rz)return;
ll mid=(lz+rz)/2;
build(now*2,lz,mid);
build(now*2+1,mid+1,rz);
}
void push_up(ll now){
maxz[now]=max(maxz[now*2],maxz[now*2+1]);
}
void insert(ll now,ll p,ll val){
if(l[now]==r[now]){
maxz[now]=max(maxz[now],val);
return;
}
ll mid=(l[now]+r[now])/2;
if(p<=mid)insert(now*2,p,val);
else insert(now*2+1,p,val);
push_up(now);
}
ll find(ll now,ll lz,ll rz){
if(l[now]>=lz&&r[now]<=rz)return maxz[now];
ll mid=(l[now]+r[now])/2,ans=0;
if(lz<=mid)ans=max(ans,find(now*2,lz,rz));
if(rz>mid)ans=max(ans,find(now*2+1,lz,rz));
return ans;
}
};
ll hd[MAXN],ed[MAXN*2],nxt[MAXN*2];
ll n,m,idx;
ll dep[MAXN],fa[MAXN][50],v[MAXN];
ll l[MAXN],r[MAXN];
struct node{
ll l,r,v,id,ans;
};
node qs[MAXN],line[MAXN];
bool cmp_r(node a,node b){
return a.r>b.r;
}
bool cmp_l(node a,node b){
return a.l<b.l;
}
bool cmp_len(node a,node b){
return a.r-a.l>b.r-b.l;
}
bool cmp_id(node a,node b){
return a.id<b.id;
}
bool cmp_k(node a,node b){
return a.v>b.v;
}
void build(ll x,ll y){
nxt[++idx]=hd[x];
ed[idx]=y;
hd[x]=idx;
}
void dfs(ll now,ll p){
fa[now][0]=p;
dep[now]=dep[p]+1;
for(int i=1;i<=20;i++)fa[now][i]=fa[fa[now][i-1]][i-1];
for(int i=hd[now];i;i=nxt[i]){
ll y=ed[i];
if(y==p)continue;
dfs(y,now);
}
}
ll lca(ll x,ll y){
if(dep[x]<dep[y])swap(x,y);
for(int i=20;i>=0;i--)if(dep[fa[x][i]]>=dep[y])x=fa[x][i];
if(x==y)return x;
for(int i=20;i>=0;i--)if(fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
deque <pair<ll,ll> > q;
node_Sagtr tr;
//void work_l(){
// sort(qs+1,qs+1+m,cmp_l);
// sort(line+1,line+n,cmp_l);
// ll now=1;
// tr.build(1,1,n);
// for(int i=1;i<=m;i++){
// while(now<n){
// if(line[now].l<=qs[i].l){
// tr.insert(1,line[now].r,line[now].v);
// now++;
// }else break;
// }
// qs[i].ans=max(qs[i].ans,tr.find(1,qs[i].l+qs[i].v-1,qs[i].r));
// }
//}
void work_r(){
sort(qs+1,qs+1+m,cmp_r);
sort(line+1,line+n,cmp_r);
ll now=1;
tr.build(1,1,n);
for(int i=1;i<=m;i++){
// cout<<i<<endl;
while(now<n){
if(line[now].r>=qs[i].r){
tr.insert(1,line[now].l,line[now].v);
now++;
}else break;
}
qs[i].ans=max(qs[i].ans,tr.find(1,1,qs[i].r-qs[i].v+1));
}
}
void work_len(){
sort(qs+1,qs+1+m,cmp_k);
sort(line+1,line+n,cmp_len);
ll now=1;
tr.build(1,1,n);
for(int i=1;i<=m;i++){
if(qs[i].v==1&&qs[i-1].v>1){
for(int j=1;j<=n;j++)tr.insert(1,j,dep[j]);
}
while(now<n){
if(line[now].r-line[now].l+1>=qs[i].v){
tr.insert(1,line[now].r,line[now].v);
now++;
}else break;
}
qs[i].ans=max(qs[i].ans,tr.find(1,qs[i].l+qs[i].v-1,qs[i].r));
}
}
int main(){
freopen("query4.in","r",stdin);
freopen("w.out","w",stdout);
n=read();
for(int i=1;i<n;i++){
ll x=read(),y=read();
build(x,y);
build(y,x);
}
// debug;
dfs(1,0);
// debug;
for(int i=1;i<n;i++)v[i]=dep[lca(i,i+1)];
q.push_back(make_pair(0,0));
for(int i=1;i<n;i++){
// cout<<i<<" "<<v[i]<<endl;
while(!q.empty()&&q.back().second>=v[i])q.pop_back();
l[i]=q.back().first+1;
q.push_back(make_pair(i,v[i]));
}
// debug;
while(!q.empty())q.pop_back();
q.push_back(make_pair(n,0));
for(int i=n-1;i;i--){
while(!q.empty()&&q.back().second>=v[i])q.pop_back();
r[i]=q.back().first;
q.push_back(make_pair(i,v[i]));
}
// debug;
for(int i=1;i<=n-1;i++){
line[i].l=l[i],line[i].r=r[i],line[i].v=v[i];
// cout<<"i:"<<i<<" dep:"<<v[i]<<endl;
}
m=read();
for(int i=1;i<=m;i++){
qs[i].l=read(),qs[i].r=read(),qs[i].v=read(),qs[i].id=i;
}
// work_l();
work_r();
work_len();
sort(qs+1,qs+1+m,cmp_id);
// for(int i=1;i<=m;i++)cout<<qs[i].ans<<endl;
for(int i=1;i<=m;i++)printf("%lld\n",qs[i].ans);
return 0;
}