题解 P9058【[Ynoi2004] rpmtdq】

· · 题解

\text{Link}

这题之前在 Ynoi 以强制在线、不带边权和 5\times 10^4 的数据范围作为一个 O(n\sqrt n) 题目出现过,这个是解法,后来被替换掉了。

现在它变成了 polylog 的题再次进入 Ynoi。

$\text{Upd2023.11.1}$:修正一处 $\min$ 写成 $\max$。 ## 题意 给出一颗 $n$ 个点的有权树,$q$ 次询问,每次询问给出 $l,r$,求 $\displaystyle\min_{l\le i<j\le r}\text{dis}(i,j)$。允许离线。 $n\le 2\times 10^5$,$q\le 10^6$。 ## 思路 最近点对问题有一个通用思路,就是找“支配点对”,也就是说,有许多的点对是没有用的。 由于树上距离涉及路径,我们可以思考点分治。对于一次分治,我们要思考这个连通块两两之间有哪些是会计入最终有效点对的。 考虑将连通块内所有点 $p$ 以二元组 $(p,a_p)$ 表示,其中 $a_p$ 表示 $\text{dis}(p,r)$,$r$ 为当前分治中心。则对于点对 $(p,q)$,我们有 $\text{dis}(p,q)\le a_p+a_q$。(为什么是 $\le$ 号?因为 $p,q$ 可能来自分治中心的同一个子树。但是这种情况我们无需特殊处理,默认 $\text{dis}(p,q)=a_p+a_q$,原因下文将给出) 我们考虑一下这层分治的有效点对 $(i,k)$,无效点对 $(i,j),(j,k)$,其中 $i<j<k$。对 $i$ 考虑 $(i,k)$ 有效相当于 $\text{dis}(i,j)>\text{dis}(i,k)$ 即 $a_j>a_k$,对 $k$ 有类似的 $a_j>a_i$,即 $a_i<a_j,a_k<a_j$。所以对于有效点对 $(l,r)$,$\max(a_l,a_r)<\displaystyle\min_{i=l+1}^{r-1}a_i$。 有一个想法是,按 $a_p$ 的值从小到大排序,初始时有一空集,依次向集合中加入对应的 $p$。设加入 $p$ 之前,集合里 $p$ 的前驱为 $v$、后继为 $w$,则 $(p,v)$ 和 $(p,w)$ 都是有效点对。容易用 `set` 维护。每个点在每个包含它的分治只会加入 $O(1)$ 组合法点对,则总合法点对数是 $O(n\log n)$ 的。 说一下不用管同子树的点对的影响的原因:同子树内的点对的距离只会更近,把距离放大了还比不过那就说明被淘汰掉的点对是无效的了。 考虑找出所有的合法点对 $(i,j),i<j$ 后怎么做,用扫描线,维护序列 $t$,扫到 $j$ 的时候 $t_i=\min(t_i,\text{dis}(i,j))$,扫到 $r$ 处理 $[l,r]$ 询问,即查询 $\displaystyle\min_{p=l}^r t_p$ 等价于单点取 $\min$ 后缀 $\min$,用树状数组即可搞定。 点分治部分每一层是 $O(s\log s)$ 的,总共是 $O(n\log^2n)$,扫描线有 $O(n\log n)$ 组修改和 $q$ 组查询,复杂度为 $O(n\log^2n+q\log n)$,总时间复杂度 $O(n\log ^2n+q\log n)$,空间复杂度 $O(n\log n+q)$。 但是这么写你会被卡常。 考虑优化找有效点对的过程,我们发现这个过程等价于按 $p$ 排序,维护 $a_p$ 的单调**不减**的单调栈,按 $p$ 顺序加入 $a_p$,在弹出栈顶所有大于 $a_p$ 的数后,设栈顶为 $a_v$,则 $(v,p)$ 为一对有效点对。我们正反做两遍即可。 时空复杂度不变。 代码: ```cpp #include<bits/stdc++.h> using namespace std; #define ll long long namespace IO{//by cyffff } #define pii pair<int,int> #define pli pair<ll,int> #define pil pair<int,ll> #define mpr make_pair #define fir first #define sec second const int N=2e5+10,M=1e6+10,INF=1e9; const ll INF_=1e18; int n,q,head[N],cnt,siz[N],top; bool del[N]; ll ans[M]; pil stk[N],stk2[N]; int top2; vector<int>use[N]; struct Edge{ int to,nxt,w; }a[N<<1]; struct Query{ int l,id; }; vector<Query>qr[N]; inline void add(int u,int v,int w){ cnt++; a[cnt].to=v; a[cnt].nxt=head[u]; a[cnt].w=w; head[u]=cnt; } inline void push(int u,int v){ if(u>v) swap(u,v); use[v].emplace_back(u); } inline pii findroot(int rt,int f,int pre){ siz[rt]=1; int mx=0; pii ans=make_pair(INF,0); for(int i=head[rt];i;i=a[i].nxt){ int t=a[i].to; if(del[t]||t==f) continue; ans=min(ans,findroot(t,rt,pre)); siz[rt]+=siz[t]; mx=max(mx,siz[t]); } ans=min(ans,mpr(max(mx,pre-siz[rt]),rt)); return ans; } inline void dfs(int rt,int f,ll s){ stk[++top]=mpr(rt,s); for(int i=head[rt];i;i=a[i].nxt){ int t=a[i].to; if(t==f||del[t]) continue; dfs(t,rt,s+a[i].w); } } inline void solve(int rt,int pre){ int root=findroot(rt,0,pre).second; int res=siz[rt]; del[root]=1; top=0; for(int i=head[root];i;i=a[i].nxt){ int t=a[i].to; if(del[t]) continue; dfs(t,root,a[i].w); } stk[++top]=mpr(root,0); sort(stk+1,stk+top+1); top2=0; for(int i=1;i<=top;i++){ int id=stk[i].fir; ll d=stk[i].sec; while(top2&&stk2[top2].sec>d) top2--; push(id,stk2[top2].fir); stk2[++top2]=stk[i]; } top2=0; for(int i=top;i>=1;i--){ int id=stk[i].fir; ll d=stk[i].sec; while(top2&&stk2[top2].sec>d) top2--; push(id,stk2[top2].fir); stk2[++top2]=stk[i]; } for(int i=head[root];i;i=a[i].nxt){ int t=a[i].to; if(del[t]) continue; solve(t,res); } } struct ST_table{ int dep[N],ind[N],lg[N<<1],cnt,st[20][N<<1]; ll de[N]; inline void dfs(int rt,int f){ dep[rt]=dep[f]+1; st[0][++cnt]=rt,ind[rt]=cnt; for(int i=head[rt];i;i=a[i].nxt){ int t=a[i].to; if(t==f) continue; de[t]=de[rt]+a[i].w; dfs(t,rt); st[0][++cnt]=rt; } } inline int cmp(int x,int y){ return dep[x]<dep[y]?x:y; } inline void init(){ dfs(1,0); for(int i=1;(1<<i)<=cnt;i++) for(int j=1;j+(1<<i)-1<=cnt;j++) st[i][j]=cmp(st[i-1][j],st[i-1][j+(1<<i-1)]); for(int i=2;i<=cnt;i++) lg[i]=lg[i>>1]+1; } inline int LCA(int x,int y){ x=ind[x],y=ind[y]; if(x>y) swap(x,y); int i=lg[y-x+1]; int p=1<<i; return cmp(st[i][x],st[i][y-p+1]); } inline ll dis(int x,int y){ return de[x]+de[y]-2*de[LCA(x,y)]; } }s; struct BIT{ #define lowbit(i) (i&-i) ll t[N]; inline void init(){ for(int i=1;i<=n;i++) t[i]=INF_; } inline void add(int p,ll v){ for(int i=p;i;i-=lowbit(i)) t[i]=min(t[i],v); } inline ll query(int p){ ll ans=INF_; for(int i=p;i<=n;i+=lowbit(i)) ans=min(ans,t[i]); return ans; } }T; int main(){ n=read(); for(int i=1;i<n;i++){ int u=read(),v=read(),w=read(); add(u,v,w),add(v,u,w); } solve(1,n); s.init(); T.init(); q=read(); for(int i=1;i<=q;i++){ int l=read(),r=read(); if(l>=r) ans[i]=-1; else qr[r].emplace_back((Query){l,i}); } for(int i=1;i<=n;i++){ sort(use[i].begin(),use[i].end()); use[i].resize(unique(use[i].begin(),use[i].end())-use[i].begin()); } for(int i=1;i<=n;i++){ for(auto t:use[i]) T.add(t,s.dis(t,i)); for(auto t:qr[i]) ans[t.id]=T.query(t.l); } for(int i=1;i<=q;i++) write(ans[i]),putc('\n'); flush(); } ```