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;
}

posted @ 2023-03-29 09:03  fzj2007  阅读(19)  评论(0编辑  收藏  举报