IOI2022 无线电信号塔
询问实际上是求类笛卡尔树上的叶子结点个数,因为祖先一定无法与后代通信
发现如果两个叶子 \(u,v\) 以 \(\text{LCA(u,v)}\) 的某一祖先 \(p\) 进行通信,那么 \(p\) 的祖先也一定能通信,保证两两能通信的关键就是一棵对于所有关键点的虚树,由于关键点之间并不存在祖先后代关系,因此笛卡尔树上的虚树大小一定是二倍叶子数 \(-1\)
求虚树大小是不好求的,但可以感受到虚树上的非叶子性质是很好的,记
\[time(u)=H_u-\max(\min\{Subtree(ls_u)\},min\{Subtree(rs_u)\})
\]
也即 \(u\) 的子树内可以选出两个信号塔通信的最小 \(D\),为了最大化答案,一定会贪心地选择所有满足 \(time(u)\ge D\) 的子树根放到虚树上,用一个主席树就可以求出
然而求的答案可能出现选出的信号塔在区间外的情况,但是因为是在笛卡尔树上,所以只会在边界出现,同样可以在主席树上二分实现
点击查看代码
#include<bits/stdc++.h>
#define N 100005
using namespace std;
int n,h[N],dif[N],tmp[N],lis;
int lson[N],rson[N],tree[N];
vector<int>vec[N];
void chkmin(int&x,int y){x>y?x=y:x;}
namespace SGT{
#define mid ((l+r)>>1)
struct Data{int ls,rs,Sum;}tr[N<<5];int rt[N],tot;
void Copy(int&k){tr[++tot]=tr[k];k=tot;}
void Modify(int&k,int l,int r,int x){
Copy(k);tr[k].Sum++;if(l==r)return;
x<=mid?Modify(tr[k].ls,l,mid,x):Modify(tr[k].rs,mid+1,r,x);
}
int Query(int k,int l,int r,int x,int y){
if(!k||l>y||r<x||x>y)return 0;
if(l>=x&&r<=y)return tr[k].Sum;
if(y<=mid)return Query(tr[k].ls,l,mid,x,y);
if(mid<x)return Query(tr[k].rs,mid+1,r,x,y);
return Query(tr[k].ls,l,mid,x,y)+Query(tr[k].rs,mid+1,r,x,y);
}
int BoundL(int k,int l,int r,int x,int y){
int ret=-1;
if(!k)return max(x,l);
if(tr[k].Sum==r-l+1)return ret;
if(x<=mid)ret=BoundL(tr[k].ls,l,mid,x,y);
if(ret==-1&&mid<y)ret=BoundL(tr[k].rs,mid+1,r,x,y);
return ret;
}
int BoundR(int k,int l,int r,int x,int y){
int ret=-1;
if(!k)return min(y,r);
if(tr[k].Sum==r-l+1)return ret;
if(mid<y)ret=BoundR(tr[k].rs,mid+1,r,x,y);
if(ret==-1&&x<=mid)ret=BoundR(tr[k].ls,l,mid,x,y);
return ret;
}
}
using namespace SGT;
namespace Sparse_Table{
int Max[18][N],Min[18][N],lg[N];
int chkmax(int x,int y){return h[x]>h[y]?x:y;}
void Init(){
for(int i=2;i<=n;i++)lg[i]=lg[i>>1]+1;
for(int i=1;i<=n;i++)Min[0][i]=h[Max[0][i]=i];
for(int j=1;j<=lg[n];j++)
for(int i=1;i+(1<<j)-1<=n;i++){
Min[j][i]=min(Min[j-1][i],Min[j-1][i+(1<<(j-1))]);
Max[j][i]=chkmax(Max[j-1][i],Max[j-1][i+(1<<(j-1))]);
}
}
int AskMax(int l,int r){
int k=lg[r-l+1];
return chkmax(Max[k][l],Max[k][r-(1<<k)+1]);
}
int AskMin(int l,int r){
int k=lg[r-l+1];
if(!(r-l+1))return 1e9+1;
return min(Min[k][l],Min[k][r-(1<<k)+1]);
}
}
using namespace Sparse_Table;
int Cartesian(int l,int r){
int Mid=AskMax(l,r);
if(l<Mid)chkmin(tree[Mid],tree[lson[Mid]=Cartesian(l,Mid-1)]);
if(Mid<r)chkmin(tree[Mid],tree[rson[Mid]=Cartesian(Mid+1,r)]);
return Mid;
}
int max_towers(int l,int r,int d){
l++;r++;
if(l==r)return 1;
int D=lower_bound(tmp+1,tmp+lis+1,d)-tmp-1;
int ans=r-l+1-Query(rt[D],1,n,l,r)+1;
int lpos=BoundL(rt[D],1,n,l,r),rpos=BoundR(rt[D],1,n,l,r);
if((~lpos)&&(~rpos)){
if(lpos==rpos)ans-=(h[lpos]-max(AskMin(l,lpos-1),AskMin(rpos+1,r))<d);
else{
ans-=(h[lpos]-AskMin(l,lpos-1)<d);
ans-=(h[rpos]-AskMin(rpos+1,r)<d);
}
}
return ans;
}
void init(int nn,vector<int>H){
n=nn;
for(int i=1;i<=n;i++)tree[i]=h[i]=H[i-1];
Init();Cartesian(1,n);
for(int i=1;i<=n;i++)dif[i]=h[i]-max(tree[lson[i]],tree[rson[i]]);
for(int i=1;i<=n;i++)if(!lson[i]||!rson[i])dif[i]=0;
for(int i=1;i<=n;i++)tmp[++lis]=dif[i];
sort(tmp+1,tmp+lis+1);lis=unique(tmp+1,tmp+lis+1)-tmp-1;
for(int i=1;i<=n;i++)
vec[lower_bound(tmp+1,tmp+lis+1,dif[i])-tmp].emplace_back(i);
for(int i=1;i<=lis;i++){
rt[i]=rt[i-1];
for(auto u:vec[i])Modify(rt[i],1,n,u);
}
}