Gym104076L Tree Distance
Gym104076L Tree Distance
题目链接。
\(\text{difficulty}={4,2.5}\)。
\(\text{tags}=点分治,扫描线\)。
没见过确实想不到。
由于查询是区间对区间,分块等数据结构并不好直接维护。考虑找一些性质,如果两个点 \(l,r(l <r)\) 的距离大于等于被其包含的两个点 \(i,j(l<i<j<r)\),那么 \(l,r\) 一定不会成为答案。
那么我们尝试找到所有可能成为答案的点对。考虑点分治,设当前分治中心为 \(x\)。对于其中一个子树中的点 \(a\),我们希望找到一些 \(b\) 满足 \(\text{dis}(a,x)\le \text{dis}(b,x)\) 并且 \(a,b\) 不在同一个子树内。
考虑如果有多个点满足条件,其中两个为 \(b,c\),我们考虑 \(b<c<a\) 的情况,那么根据 \(b,c\) 的条件显然有 \(\text{dis}(b,c)\le \text{dis}(b,a)\),所以保留 \((b,a)\) 是没有意义的。对于 \(a<c<b\) 的情况同理。
那么实际上需要对于每个 \(a\) 找到与其不在同一个子树内并且 \(\text{dis}(x,b)\le \text{dis}(x,a)\) 的前驱 \(b\) 和后继 \(b\)。进一步发现不在同一个子树的限制意义不大,因为如果找到了同一子树的点 \(b\),那么 \(b\) 一定比 \(a\) 更优。
那么我们只需要将分治中心 \(x\) 内的所有点按照深度排序,然后正着做一遍单调栈求出每个点的前驱 \(b\),再反着做一遍单调栈求出每个点的后继 \(b\) 即可找到所有关键点。
由于每一层每个 \(a\) 只会找到两个 \(b\),所以总点对个数是 \(\mathcal{O}(n \log n)\) 级别的。
接下来就是一个简单的二维数点,将点对 \((a,b)(a<b)\) 挂在 \(b\) 上,将询问 \([l,r]\) 挂在 \(r\) 上,然后从左往右扫,每次单点插入,求后缀 \(\max\),使用树状数组维护即可。
总时间复杂度 \(\mathcal{O}(n \log^2 n+q\log n)\)。
code
#include<bits/stdc++.h>
using namespace std;
namespace IO{
template<typename T>inline bool read(T &x){
x=0;
char ch=getchar();
bool flag=0,ret=0;
while(ch<'0'||ch>'9') flag=flag||(ch=='-'),ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar(),ret=1;
x=flag?-x:x;
return ret;
}
template<typename T,typename ...Args>inline bool read(T& a,Args& ...args){
return read(a)&&read(args...);
}
template<typename T>void prt(T x){
if(x>9) prt(x/10);
putchar(x%10+'0');
}
template<typename T>inline void put(T x){
if(x<0) putchar('-'),x=-x;
prt(x);
}
template<typename T>inline void put(char ch,T x){
if(x<0) putchar('-'),x=-x;
prt(x);
putchar(ch);
}
template<typename T,typename ...Args>inline void put(T a,Args ...args){
put(a);
put(args...);
}
template<typename T,typename ...Args>inline void put(const char ch,T a,Args ...args){
put(ch,a);
put(ch,args...);
}
inline void put(string s){
for(int i=0,sz=s.length();i<sz;i++) putchar(s[i]);
}
inline void put(const char* s){
for(int i=0,sz=strlen(s);i<sz;i++) putchar(s[i]);
}
}
using namespace IO;
#define N 200005
#define ll long long
int n,q,head[N],cnt;
ll ans[N*5];
struct edge{
int v,nxt,val;
}e[N<<1];
inline void add(int u,int v,int w){
e[++cnt]=(edge){v,head[u],w},head[u]=cnt;
}
vector<pair<int,ll> >que[N],ins[N],p;
bool vis[N];
int st[N],tp;
inline int get_siz(int x,int fa){
if(vis[x]) return 0;
int siz=1;
for(int i=head[x];i;i=e[i].nxt)
if(e[i].v!=fa) siz+=get_siz(e[i].v,x);
return siz;
}
inline int get_wc(int x,int fa,int sum,int &wc){
if(vis[x]) return 0;
int siz=1,ms=0;
for(int i=head[x],t;i;i=e[i].nxt)
if(e[i].v!=fa) t=get_wc(e[i].v,x,sum,wc),siz+=t,ms=max(ms,t);
if(max(ms,sum-siz)<=sum/2) wc=x;
return siz;
}
inline void get_dist(int x,int fa,ll pre){
p.emplace_back(x,pre);
for(int i=head[x];i;i=e[i].nxt)
if(e[i].v!=fa&&!vis[e[i].v]) get_dist(e[i].v,x,pre+e[i].val);
}
inline void solve(int x){
if(vis[x]) return;
p.clear();
get_wc(x,0,get_siz(x,0),x),vis[x]=1;
get_dist(x,0,0),sort(p.begin(),p.end());
for(int i=0,tp=0;i<p.size();i++){
while(tp&&p[st[tp]].second>p[i].second) tp--;
if(tp) ins[p[i].first].emplace_back(p[st[tp]].first,p[i].second+p[st[tp]].second);
st[++tp]=i;
}
for(int i=p.size()-1,tp=0;~i;i--){
while(tp&&p[st[tp]].second>p[i].second) tp--;
if(tp) ins[p[st[tp]].first].emplace_back(p[i].first,p[i].second+p[st[tp]].second);
st[++tp]=i;
}
for(int i=head[x];i;i=e[i].nxt) solve(e[i].v);
}
struct BIT{
ll c[N];
BIT(){memset(c,0x3f,sizeof(c));}
#define lowbit(x) (x&-x)
inline void update(int x,ll v){
for(;x;x^=lowbit(x)) c[x]=min(c[x],v);
}
inline ll query(int x){
ll res=0x3f3f3f3f3f3f3f3f;
for(;x<=n;x+=lowbit(x)) res=min(res,c[x]);
return res;
}
#undef lowbit
}B;
int main(){
read(n);
for(int i=1,u,v,w;i<n;i++)
read(u,v,w),add(u,v,w),add(v,u,w);
solve(1),read(q);
for(int i=1,l,r;i<=q;i++)
read(l,r),que[r].emplace_back(l,i);
for(int i=1;i<=n;i++){
for(auto tmp:ins[i]) B.update(tmp.first,tmp.second);
for(auto tmp:que[i]) ans[tmp.second]=B.query(tmp.first);
}
for(int i=1;i<=q;i++) put('\n',ans[i]>=0x3f3f3f3f3f3f3f3f?-1:ans[i]);
return 0;
}