[Ynoi2004] Range Pair Minimum Tree Distance Query
题面
题目描述
给定一棵有边权的无根树,定义 \(\operatorname{dist}(i,j)\) 代表树上点 \(i\) 和点 \(j\) 之间的距离。
有一些询问,对于每一组询问,给出 \(l,r\),求 \(\displaystyle\min_{l\leq i \lt j \leq r} \operatorname{dist}(i,j)\)。
数据范围
- \(1\leq n\leq2\times 10^5\)。
- \(1\leq q\leq 10^6\)。
- 边权 \(\leq 10^9\)
题解
询问看起来是二维前缀 min 的形式,然而可以产生贡献的点有多达 \(O(n^2)\) 个。
考虑是否可以删去一些没用的点,即我们希望尽可能的删去 \(\exists i\leq i'\lt j' \leq j,\operatorname{dist}(i',j')<\operatorname{dist}(i,j)\) 的 \((i,j)\)。
考虑点分治,令分治中心为 \(root\),计算 \(d_u=\operatorname{dist}(root,u)\),对于每个点 \(u\),我们每次仅仅保留 \((l_u,u)\) 和 \((u,r_u)\),其中 \(l_u=\max\{ v|v\lt u \wedge d_v\leq d_u\}\),\(r_u=\min\{ v|v\gt u \wedge d_v\leq d_u\}\)。这样总点数减少到了 \(O(n\log n)\)。
为什么这样是对的
以 \(l_u\) 举例,采用反证法。
假设存在 \(v\) 使得 \(l_u \lt v\lt u\) 且 \((v,u)\) 的贡献漏了。由于 \((v,u)\) 会产生贡献且 \(l_u\neq v\),说明 \(\displaystyle d_u\lt d_v\lt \min_{p=u+1}^{v-1} d_p\),根据 \(r\) 的定义可知 \(r_v\geq u\)。因为 \((v,u)\) 没有被算到,所以 \(r_v\neq u\),即 \(r_v>u\),说明 \(d_v>d_u\),与 \(l_u \lt v\lt u\) 矛盾。
假设存在 \(v\) 使得 \(\operatorname{dist}(v,u) \lt \operatorname{dist}(l_u,u)\) 且 \((v,u)\) 的贡献漏了。根据 \(l_u\) 的定义可知,有 \(v \lt l_u\lt u\)。由于 \((v,u)\) 会产生贡献且 \(l_u\neq v\),说明 \(\displaystyle d_u,d_v\lt \min_{p=u+1}^{v-1} d_p\),而这与 \(d_{l_u}\leq d_u\) 相悖。
所以这么选点是对的。
然后就是离线扫描线二维前缀 min,时间复杂度 \(O(n\log^2 n+q\log n)\)。
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
using ll=long long;
const int N=2e5+9;
const int Q=1e6+9;
const int lgN=2e1;
const int NlgN=2e6+9;
const ll inf=1e18+7;
int fi[N],ne[N<<1],to[N<<1],w[N<<1],adj;
inline void AddEdge(int x,int y,int z){
ne[++adj]=fi[x];
fi[x]=adj;
to[adj]=y;
w[adj]=z;
}
int fa[N],elr[N<<1],pos[N],ecnt;
ll dep[N];
inline void DFS(int x){
elr[++ecnt]=x;
pos[x]=ecnt;
for(int i=fi[x];i;i=ne[i]){
int y=to[i];
if(y==fa[x]) continue ;
dep[y]=dep[x]+w[i];
fa[y]=x;
DFS(y);
elr[++ecnt]=x;
}
}
int mn[N<<1][lgN],lg[N<<1];
inline void Init(){
for(int i=2;i<=ecnt;i++) lg[i]=lg[i>>1]+1;
for(int i=1;i<=ecnt;i++) mn[i][0]=pos[elr[i]];
for(int k=1;k<=lg[ecnt];k++){
for(int i=1;i<=ecnt-(1<<k)+1;i++) mn[i][k]=min(mn[i][k-1],mn[i+(1<<k-1)][k-1]);
}
}
inline int LCA(int u,int v){
u=pos[u],v=pos[v];
if(u>v) swap(u,v);
int k=lg[v-u+1];
return elr[min(mn[u][k],mn[v-(1<<k)+1][k])];
}
inline ll Dist(int u,int v){return dep[u]+dep[v]-2*dep[LCA(u,v)];}
int vis[N],siz[N];
inline void GetGrv(int x,int f,int tot,int &grv){
bool flag=1;
siz[x]=1;
for(int i=fi[x];i;i=ne[i]){
int y=to[i];
if(vis[y]) continue ;
if(y==f) continue ;
GetGrv(y,x,tot,grv);
siz[x]+=siz[y];
if(siz[y]>tot/2) flag=0;
}
if(tot-siz[x]>tot/2) flag=0;
if(flag) grv=x;
}
inline void GetMem(int x,int f,vector<int> &v){
siz[x]=1;
v.push_back(x);
for(int i=fi[x];i;i=ne[i]){
int y=to[i];
if(vis[y]) continue ;
if(y==f) continue ;
GetMem(y,x,v);
siz[x]+=siz[y];
}
}
int pl[NlgN<<1],pr[NlgN<<1],po[NlgN<<1],pcnt;
inline void Divide(int x,int tot){
GetGrv(x,0,tot,x);
vis[x]=1;
vector<int> v,l(tot,0),r(tot,0);
vector<ll> d(tot);
GetMem(x,0,v);
sort(v.begin(),v.end());
for(int i=0;i<tot;i++) d[i]=Dist(x,v[i]);
vector<int> stk;
for(int i=0;i<tot;i++){
while(stk.size()&&d[i]<d[stk.back()]) stk.pop_back();
if(stk.size()) l[i]=v[stk.back()];
stk.push_back(i);
}
stk.clear();
for(int i=tot-1;~i;i--){
while(stk.size()&&d[i]<d[stk.back()]) stk.pop_back();
if(stk.size()) r[i]=v[stk.back()];
stk.push_back(i);
}
for(int i=0;i<tot;i++){
if(l[i]) pcnt++,pl[pcnt]=l[i],pr[pcnt]=v[i];
if(r[i]) pcnt++,pl[pcnt]=v[i],pr[pcnt]=r[i];
}
for(int i=fi[x];i;i=ne[i]){
int y=to[i];
if(vis[y]) continue ;
Divide(y,siz[y]);
}
}
int n;
ll tr[N];
inline void Modify(int x,ll k){
while(x<=n){
tr[x]=min(tr[x],k);
x+=x&-x;
}
}
inline ll Query(int x){
ll res=inf;
while(x){
res=min(res,tr[x]);
x&=x-1;
}
return res;
}
int ql[Q],qr[Q],qo[Q],q;
ll ans[Q];
signed main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n;
for(int i=1,u,v,w;i<n;i++) cin>>u>>v>>w,AddEdge(u,v,w),AddEdge(v,u,w);
cin>>q;
for(int i=1;i<=q;i++) cin>>ql[i]>>qr[i];
DFS(1),Init();
Divide(1,n);
for(int i=1;i<=pcnt;i++) po[i]=i;
sort(po+1,po+pcnt+1,[](int i,int j){return pr[i]<pr[j];});
for(int i=1;i<=q;i++) qo[i]=i;
sort(qo+1,qo+q+1,[](int i,int j){return qr[i]<qr[j];});
for(int i=1;i<=n;i++) tr[i]=inf;
for(int i=1,j=1,k=1;i<=n;i++){
while(j<=pcnt&&pr[po[j]]<=i) Modify(n-pl[po[j]]+1,Dist(pl[po[j]],pr[po[j]])),j++;
while(k<=q&&qr[qo[k]]<=i) ans[qo[k]]=Query(n-ql[qo[k]]+1),k++;
}
for(int i=1;i<=q;i++) cout<<(ans[i]!=inf?ans[i]:-1)<<endl;
return 0;
}

浙公网安备 33010602011771号