支配对
yangzichen1203
·
·
算法·理论
参考资料:https://www.luogu.com.cn/article/rmdqa1b8
1
P5926 [JSOI2009] 面试的考验
CF765F Souvenirs
CF1793F Rebrending
$$\min_{l\le i<j\le r}|a_i-a_j|$$
:::success[Solution]
对每个点考虑 $i$ 考虑 $i<j,a_i\le a_j$ 的点对 $(i,j)$。
$i<j,a_i\ge a_j$ 同理。
考虑这些点对之间的支配关系。
若 $(i,j)$ 是支配对,$i<j<k$,$(i,k)$ 是支配对一定需要
$$a_k-a_i<a_j-a_i,a_k-a_i<a_j-a_k$$
$$a_k<a_j,a_k<\dfrac{a_i+a_j}{2}$$
$$a_k<\dfrac{a_i+a_j}{2}$$
我们不要求枚举所有真正不可被任意点对支配的点对,只需要枚举一个充分不必要的候选集。
对于 $i$ 每次向右找第一个符合条件的 $j$,然后更新条件,最多找 $O(\log V)$ 次。
使用动态开点权值线段树维护每个权值所在位置,记录最小位置,从右到左扫时动态插入即可单次操作 $O(\log n)$。
一共最多存在 $O(n\log V)$ 个支配对,将询问离线,通过扫描线解决一维,树状数组维护另一维。
时间复杂度:$O(n\log V\log n+m\log n)$。
空间复杂度:$O(n\log V)$。
:::
:::success[Code]
由于是很久前写的,与题解思路相同,细节上左右方向相反。
```cpp
#include<bits/stdc++.h>
#define For(i,j,k) for(int i=(j);i<=(k);i++)
#define dFor(i,j,k) for(int i=(j);i>=(k);i--)
using namespace std;
#define MAXN 100005
#define V 1000000000
#define inf (1<<30)
int n,m,a[MAXN];
struct Node{
int ls,rs,max;
}tr[MAXN*32];
int cnt=0,rt=0;
inline int newnode(){
tr[++cnt]={0,0,0};
return cnt;
}
void insert(int &c,int L,int R,int x,int k){
if(!c) c=newnode();
tr[c].max=max(tr[c].max,k);
if(L==R) return ;
int mid=(L+R)>>1;
if(x<=mid) insert(tr[c].ls,L,mid,x,k);
else insert(tr[c].rs,mid+1,R,x,k);
}
int query(int c,int L,int R,int l,int r){
if(!c||l>r) return 0;
if(l<=L&&r>=R) return tr[c].max;
int mid=(L+R)>>1;
int ans=0;
if(l<=mid) ans=max(ans,query(tr[c].ls,L,mid,l,r));
if(r>mid) ans=max(ans,query(tr[c].rs,mid+1,R,l,r));
return ans;
}
int b[MAXN];
void add(int x,int k){
while(x){
b[x]=min(b[x],k);
x-=x&-x;
}
}
int query(int x){
int ans=inf;
while(x<=n){
ans=min(ans,b[x]);
x+=x&-x;
}
return ans;
}
struct Que{
int l,r,id;
}q[MAXN];
int ans[MAXN];
void solve(){
For(i,1,n){
b[i]=inf;
}
cnt=0;rt=0;
int now=1;
For(i,1,n){
int l=1,r=a[i]-1;
while(1){
int x=query(rt,1,V,l,r);
if(x==0) break;
add(x,a[i]-a[x]);
l=(a[i]+a[x])/2+1;
}
insert(rt,1,V,a[i],i);
while(now<=m&&q[now].r==i){
ans[q[now].id]=min(ans[q[now].id],query(q[now].l));
now++;
}
}
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n>>m;
For(i,1,n){
cin>>a[i];
}
For(i,1,m){
cin>>q[i].l>>q[i].r;
q[i].id=i;
ans[i]=inf;
}
sort(q+1,q+m+1,[&](Que x,Que y){
return x.r<y.r;
});
solve();
For(i,1,n){
a[i]=V+1-a[i];
}
solve();
For(i,1,m){
cout<<ans[i]<<'\n';
}
return 0;
}
```
:::
# 2
[P7880 [Ynoi2006] rldcot](https://www.luogu.com.cn/problem/P7880):
$n$ 个点的树,点带权,$m$ 次询问 $[l,r]$,求
$$|\{a_{LCA(x,y)}|l\le x\le y\le r\}|$$
:::success[Solution]
抽象为二维平面矩形数颜色,则时间复杂度与点数强相关。
总共有 $O(n^2)$ 个点,考虑减小有效点数量。
如果 $l_1\le l_2\le r_2\le r_1,LCA(l_1,r_1)=LCA(l_2,r_2)$,则称 $(l_2,r_2)$ 支配了 $(l_1,r_1)$,我们只保留 $(l_2,r_2)$ 即可。
考虑用 dsu on tree 找出这些支配对。
对于当前递归,对每棵子树 $u$ 考虑其左侧子树的点集,则只有其集合内前驱 $v$ 后继 $w$ 会产生支配对 $(v,u),(u,w)$。
需要注意的是对于整棵子树,先查找前驱后继再插入点。
支配对数 $O(n\log n)$,用平衡树维护集合,时间复杂度 $O(n\log^2 n)$。
二维平面矩形数颜色就直接对询问离线下来,按 $r$ 排序,只记录每种颜色最后出现的位置,树状数组统计贡献即可。
时间复杂度:$O(n\log^2 n+m\log n)$。
空间复杂度:$O(n\log n+m)$。
:::
:::success[Code]
```cpp
#include<bits/stdc++.h>
#define For(i,j,k) for(int i=(j);i<=(k);i++)
#define dFor(i,j,k) for(int i=(j);i>=(k);i--)
using namespace std;
#define MAXN 100005
#define MAXM 500005
int n,m;
vector<pair<int,int>> e[MAXN];
long long a[MAXN];
int fa[MAXN],sz[MAXN],son[MAXN];
void dfs1(int u,int f){
fa[u]=f;
sz[u]=1;
int Max=0;
for(auto [v,d]:e[u]){
if(v==f) continue;
a[v]=a[u]+d;
dfs1(v,u);
sz[u]+=sz[v];
if(sz[v]>Max){
Max=sz[v];
son[u]=v;
}
}
}
int dfn[MAXN],pos[MAXN],tot=0,top[MAXN];
void dfs2(int u,int t){
dfn[u]=++tot;
pos[tot]=u;
top[u]=t;
if(!son[u]) return ;
dfs2(son[u],t);
for(auto [v,d]:e[u]){
if(v==fa[u]||v==son[u]) continue;
dfs2(v,v);
}
}
int V;
struct Que{
int l,op,id;
};
vector<Que> q[MAXN];
void init(){
dfs1(1,0);
dfs2(1,1);
vector<long long> v;
For(i,1,n){
v.push_back(a[i]);
}
sort(v.begin(),v.end());
v.erase(unique(v.begin(),v.end()),v.end());
V=v.size();
For(i,1,n){
a[i]=lower_bound(v.begin(),v.end(),a[i])-v.begin()+1;
}
set<int> s;
auto calc=[&](int x,int a){
auto it=s.lower_bound(x);
if(it!=s.begin()){
q[x].push_back({*prev(it),0,a});
}
it=s.upper_bound(x);
if(it!=s.end()){
q[*it].push_back({x,0,a});
}
};
dFor(i,n,1){
int u=pos[i];
for(auto [v,d]:e[u]){
if(v==fa[u]||v==son[u]) continue;
For(j,dfn[v],dfn[v]+sz[v]-1){
calc(pos[j],a[u]);
}
For(j,dfn[v],dfn[v]+sz[v]-1){
s.insert(pos[j]);
}
}
s.insert(u);
q[u].push_back({u,0,(int)a[u]});
if(u==top[u]){
For(i,dfn[u],dfn[u]+sz[u]-1){
s.erase(s.find(pos[i]));
}
}
}
}
int tr[MAXN];
void add(int x,int k){
while(x){
tr[x]+=k;
x-=x&-x;
}
}
int query(int x){
int ans=0;
while(x<=n){
ans+=tr[x];
x+=x&-x;
}
return ans;
}
int lst[MAXN],ans[MAXM];
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n>>m;
For(i,1,n-1){
int u,v,d;
cin>>u>>v>>d;
e[u].push_back({v,d});
e[v].push_back({u,d});
}
init();
For(i,1,m){
int l,r;
cin>>l>>r;
q[r].push_back({l,1,i});
}
For(r,1,n){
for(auto [l,op,id]:q[r]){
if(op==0){
if(l>lst[id]){
if(lst[id]){
add(lst[id],-1);
}
add(l,1);
lst[id]=l;
}
}else{
ans[id]=query(l);
}
}
}
For(i,1,m){
cout<<ans[i]<<'\n';
}
return 0;
}
```
:::
# 3
[P8528 [Ynoi2003] 铃原露露](https://www.luogu.com.cn/problem/P8528):
$n$ 个点的树,$m$ 次询问 $[l,r]$,求
$$|\{(L,R)|l\le L\le R\le r,\forall L\le a_x,a_y\le R,L\le a_{LCA(x,y)}\le R\}|$$
:::success[Solution]
合法交等价于非法并。
令 $a_u<a_v$,对于点对 $(u,v)$,设 $w=LCA(u,v)$,考虑其对查询区间的贡献。
若 $a_w\in [1,a_u-1]$,则使得 $L\in [a_w+1,a_u],R\in [a_v,n]$ 非法。
若 $a_w\in [a_u,a_v]$,则无贡献。
若 $a_w\in [a_v+1,n]$,则使得 $L\in [1,a_u],R\in [a_v,a_w-1]$ 非法。
考虑支配对关系,若 $L_{(u_1,v_1)}\subseteq L_{(u_2,v_2)},R_{(u_1,v_1)}\subseteq R_{(u_2,v_2)}$,则 $(u_1,v_1)$ 被 $(u_2,v_2)$ 支配。
看到 LCA,考虑使用 dsu on tree 找到所有支配对。
对于当前递归,根为 $w$,设当前枚举点 $u$。
假设 $a_u<a_v$,$a_u>a_v$ 同理。
若 $a_u<a_w$,取 $a_u$ 的后继 $a_v$,若 $a_v<a_w$,则存在支配对 $(u,v)$,否则不存在此类支配对。
若 $a_u>a_w$,取 $a_u$ 的后继 $a_v$,则存在支配对 $(u,v)$。
同样需要注意对整棵子树先查询支配对再加入点。
支配对数 $O(n\log n)$,用平衡树维护集合,时间复杂度 $O(n\log^2 n)$。
查询就等价于对二维平面矩形覆盖,矩形求和,对一维差分后扫描线,另一维用线段树维护即可。
线段树维护区间最小值,区间最小值出现次数,区间加标记,区间历史合法次数,区间时间差。
注意修改矩形是一定全部满足 $l\le r$,查询矩形一半不满足 $l\le r$,做完后直接减掉即可。
时间复杂度:$O(n\log^2 n+m\log n)$。
空间复杂度:$O(n\log n+m)$。
:::
:::success[Code]
```cpp
#include<bits/stdc++.h>
#define For(i,j,k) for(int i=(j);i<=(k);i++)
#define dFor(i,j,k) for(int i=(j);i>=(k);i--)
using namespace std;
#define MAXN 200005
namespace SGT{
struct Tree{
int l,r,min,cnt,tag1,tag2;
long long sum;
}tr[MAXN*4];
inline void update(int c){
tr[c].min=min(tr[c*2].min,tr[c*2+1].min);
tr[c].cnt=0;
if(tr[c*2].min==tr[c].min) tr[c].cnt+=tr[c*2].cnt;
if(tr[c*2+1].min==tr[c].min) tr[c].cnt+=tr[c*2+1].cnt;
tr[c].sum=tr[c*2].sum+tr[c*2+1].sum;
}
inline void tag1(int c,int k){
tr[c].min+=k;
tr[c].tag1+=k;
}
inline void tag2(int c,int k){
tr[c].sum+=1ll*k*tr[c].cnt;
tr[c].tag2+=k;
}
inline void down(int c){
if(tr[c].tag1){
tag1(c*2,tr[c].tag1);
tag1(c*2+1,tr[c].tag1);
tr[c].tag1=0;
}
if(tr[c].tag2){
if(tr[c*2].min==tr[c].min){
tag2(c*2,tr[c].tag2);
}
if(tr[c*2+1].min==tr[c].min){
tag2(c*2+1,tr[c].tag2);
}
tr[c].tag2=0;
}
}
void build(int c,int L,int R){
tr[c].l=L;tr[c].r=R;
if(L==R){
tr[c].cnt=1;
return ;
}
int mid=(L+R)>>1;
build(c*2,L,mid);
build(c*2+1,mid+1,R);
update(c);
}
void add(int c,int L,int R,int k){
if(tr[c].l>=L&&tr[c].r<=R){
tag1(c,k);
return ;
}
down(c);
int mid=(tr[c].l+tr[c].r)>>1;
if(L<=mid) add(c*2,L,R,k);
if(R>mid) add(c*2+1,L,R,k);
update(c);
}
inline void modify(){
if(tr[1].min==0){
tag2(1,1);
}
}
long long query(int c,int L,int R){
if(tr[c].l>=L&&tr[c].r<=R){
return tr[c].sum;
}
down(c);
int mid=(tr[c].l+tr[c].r)>>1;
long long ans=0;
if(L<=mid) ans+=query(c*2,L,R);
if(R>mid) ans+=query(c*2+1,L,R);
return ans;
}
}
int n,m,a[MAXN],fa[MAXN];
vector<int> e[MAXN];
int sz[MAXN],son[MAXN];
void dfs1(int u){
sz[u]=1;
int Max=0;
for(int v:e[u]){
dfs1(v);
sz[u]+=sz[v];
if(sz[v]>Max){
Max=sz[v];
son[u]=v;
}
}
}
int dfn[MAXN],pos[MAXN],tot=0,top[MAXN];
void dfs2(int u,int t){
dfn[u]=++tot;
pos[tot]=u;
top[u]=t;
if(!son[u]) return ;
dfs2(son[u],t);
for(int v:e[u]){
if(v==son[u]) continue ;
dfs2(v,v);
}
}
struct Que{
int l,r,op,id;
};
vector<Que> q[MAXN];
void init(){
dfs1(1);
dfs2(1,1);
set<int> s;
auto calc=[&](int au,int aw){
auto it=s.upper_bound(au);
if(it!=s.end()){
int av=*it;
if(au<aw){
if(av<aw){
q[1].push_back({av,aw-1,1,0});
q[au+1].push_back({av,aw-1,-1,0});
}
}else{
q[aw+1].push_back({av,n,1,0});
q[au+1].push_back({av,n,-1,0});
}
}
it=s.lower_bound(au);
if(it!=s.begin()){
int av=*prev(it);
if(au>aw){
if(av>aw){
q[aw+1].push_back({au,n,1,0});
q[av+1].push_back({au,n,-1,0});
}
}else{
q[1].push_back({au,aw-1,1,0});
q[av+1].push_back({au,aw-1,-1,0});
}
}
};
dFor(i,n,1){
int u=pos[i];
for(int v:e[u]){
if(v==son[u]) continue ;
For(j,dfn[v],dfn[v]+sz[v]-1){
calc(a[pos[j]],a[u]);
}
For(j,dfn[v],dfn[v]+sz[v]-1){
s.insert(a[pos[j]]);
}
}
s.insert(a[u]);
if(u==top[u]){
For(j,dfn[u],dfn[u]+sz[u]-1){
s.erase(a[pos[j]]);
}
}
}
}
long long ans[MAXN];
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n>>m;
For(i,1,n){
cin>>a[i];
}
For(i,2,n){
cin>>fa[i];
e[fa[i]].push_back(i);
}
init();
For(i,1,m){
int l,r;
cin>>l>>r;
q[l-1].push_back({l,r,-1,i});
q[r].push_back({l,r,1,i});
ans[i]=-1ll*(r-l)*(r-l+1)/2;
}
SGT::build(1,1,n);
For(i,1,n){
for(auto [l,r,op,id]:q[i]){
if(id==0){
SGT::add(1,l,r,op);
}
}
SGT::modify();
for(auto [l,r,op,id]:q[i]){
if(id!=0){
ans[id]+=op*SGT::query(1,l,r);
}
}
}
For(i,1,m){
cout<<ans[i]<<'\n';
}
return 0;
}
```
:::
# 4
[P11364 [NOIP2024] 树上查询](https://www.luogu.com.cn/problem/P11364)
$n$ 个点的树,$m$ 次询问 $[l,r],k$,求
$$\max_{l\le L\le R\le r,R-L+1\ge k}dep_{LCA([L,R])}$$
:::success[Solution]
注意到结论
$$dep_{[l,r]}=\min_{i\in[l,r-1]}dep_{LCA(i,i+1)}$$
设 $a_i=dep_{LCA(i,i+1)}$,则
$$ans=\max_{l\le L\le R\le r,R-L+1\ge k}\min_{i\in[L,R-1]}a_i$$
对于 $k=1$ 就是区间最大值。
我们将每个区间 $[l,r]$ 视为一个点对,则考虑如何支配。
若 $l_1\le l_2\le r_2\le r_1,\min_{i\in[l_1,r_1]}a_i=\min_{i\in[l_2,r_2]}a_i$,则 $[l_1,r_1]$ 支配 $[l_2,r_2]$。
对于每个位置 $i$,单调栈找到左右侧第一个比它小的位置 $pre_i,suf_i$,则支配对为 $[pre_i+1,suf_i]$,一共 $O(n)$ 对。
问题转换为查询所有与查询区间支交长度 $\ge k$ 的支配对权值最大值。
正确性证明:虽然交不一定包含原最小值,但其实际更大的最小值一定被另一个支配对统计到,所以答案不会受影响。
我们将询问离线下来,考虑做二维数点,设查询为 $[l,r],k$,支配对为 $[x,y]$。
Case 1:
$$\begin{cases}x\le r-k+1\\y\ge r\end{cases}$$
对 $x$ 扫描线,$y$ 树状数组查询。
Case 2:
$$\begin{cases}x\le l\\y\ge l+k-1\end{cases}$$
对 $x$ 扫描线,$y$ 树状数组查询。
Case 3:
$$\begin{cases}l\le x\le r-k+1\\y-x+1\ge k\end{cases}$$
对 $y-x+1$ 扫描线,$x$ 线段树查询。
时间复杂度:$O(n\log n+m\log n)$。
空间复杂度:$O(n+m)$。
:::
:::success[Code]
```cpp
#include<bits/stdc++.h>
#define For(i,j,k) for(int i=(j);i<=(k);i++)
#define dFor(i,j,k) for(int i=(j);i>=(k);i--)
using namespace std;
#define MAXN 500005
#define MAXM 20
int n;
vector<int> e[MAXN];
int dfn[MAXN],tot=0,dep[MAXN];
int f[MAXM][MAXN],lg[MAXN];
void dfs(int u,int fa){
dfn[u]=++tot;
f[0][tot]=fa;
dep[u]=dep[fa]+1;
for(int v:e[u]){
if(v==fa) continue ;
dfs(v,u);
}
}
inline int get(int x,int y){
return dfn[x]<dfn[y]?x:y;
}
inline int LCA(int x,int y){
if(x==y) return x;
x=dfn[x];
y=dfn[y];
if(x>y) swap(x,y);
x++;
int k=lg[y-x+1];
return get(f[k][x],f[k][y-(1<<k)+1]);
}
inline int qmax(int l,int r){
int k=lg[r-l+1];
return max(f[k][l],f[k][r-(1<<k)+1]);
}
int a[MAXN];
int pre[MAXN],suf[MAXN];
struct Que{
int x,y,op,id;
};
vector<Que> qx[MAXN],qk[MAXN];
void init(){
dfs(1,0);
For(i,2,n){
lg[i]=lg[i>>1]+1;
}
For(i,1,lg[n]){
For(j,1,n-(1<<i)+1){
f[i][j]=get(f[i-1][j],f[i-1][j+(1<<(i-1))]);
}
}
For(i,1,n-1){
a[i]=dep[LCA(i,i+1)];
}
For(i,1,n){
f[0][i]=dep[i];
}
For(i,1,lg[n]){
For(j,1,n-(1<<i)+1){
f[i][j]=max(f[i-1][j],f[i-1][j+(1<<(i-1))]);
}
}
stack<int> s;
s.push(0);
For(i,1,n-1){
while(a[s.top()]>=a[i]) s.pop();
pre[i]=s.top();
s.push(i);
}
while(!s.empty()) s.pop();
s.push(n);
dFor(i,n-1,1){
while(a[s.top()]>=a[i]) s.pop();
suf[i]=s.top();
s.push(i);
}
For(i,1,n-1){
int x=pre[i]+1,y=suf[i];
qx[x].push_back({x,y,0,a[i]});
qk[y-x+1].push_back({x,y,0,a[i]});
}
}
int m,ans[MAXN];
namespace Bit{
int tr[MAXN];
void add(int x,int k){
while(x){
tr[x]=max(tr[x],k);
x-=x&-x;
}
}
int query(int x){
int ans=0;
while(x<=n){
ans=max(ans,tr[x]);
x+=x&-x;
}
return ans;
}
}
namespace SGT{
struct Tree{
int max;
}tr[MAXN*4];
inline void update(int c){
tr[c].max=max(tr[c*2].max,tr[c*2+1].max);
}
void modify(int c,int L,int R,int x,int k){
if(L==R){
tr[c].max=max(tr[c].max,k);
return ;
}
int mid=(L+R)>>1;
if(x<=mid) modify(c*2,L,mid,x,k);
else modify(c*2+1,mid+1,R,x,k);
update(c);
}
int query(int c,int L,int R,int l,int r){
if(l<=L&&r>=R){
return tr[c].max;
}
int mid=(L+R)>>1;
int ans=0;
if(l<=mid) ans=max(ans,query(c*2,L,mid,l,r));
if(r>mid) ans=max(ans,query(c*2+1,mid+1,R,l,r));
return ans;
}
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n;
For(i,1,n-1){
int u,v;
cin>>u>>v;
e[u].push_back(v);
e[v].push_back(u);
}
init();
cin>>m;
For(i,1,m){
int l,r,k;
cin>>l>>r>>k;
if(k==1){
ans[i]=qmax(l,r);
continue ;
}
qx[r-k+1].push_back({r-k+1,r,1,i});
qx[l].push_back({l,l+k-1,1,i});
qk[k].push_back({l,r-k+1,1,i});
}
For(x,1,n){
for(auto [x,y,op,id]:qx[x]){
if(op==0){
Bit::add(y,id);
}else{
ans[id]=max(ans[id],Bit::query(y));
}
}
}
dFor(k,n,1){
for(auto [x,y,op,id]:qk[k]){
if(op==0){
SGT::modify(1,1,n,x,id);
}else{
ans[id]=max(ans[id],SGT::query(1,1,n,x,y));
}
}
}
For(i,1,m){
cout<<ans[i]<<'\n';
}
return 0;
}
```
:::
# 5
[P9058 [Ynoi2004] rpmtdq](https://www.luogu.com.cn/problem/P9058)
$n$ 个点的树,$m$ 次询问 $[l,r]$,求
$$\min_{l\le i<j\le r}dis(i,j)$$
:::success[Solution]
点分治,对于每一层,设重心为 $r$,设 $d_u=dis(r,u)$。
考虑 $(l,r)$ 之间的支配关系。
对于 $l<u<r$,若 $(l,r)$ 为支配对,则
$$d_l+d_r<d_u+d_l,d_l+d_r<d_u+d_r$$
$$d_l<d_u,d_r<d_u$$
则 $(l,r)$ 为支配对的必要条件是
$$\max(d_l,d_r)<\min_{l<u<r}d_u$$
如果 $l,r$ 在同一子树内,那么它们会在下层被统计到,整个过程一定充分。需要注意的是不能在本层用 $d_l+d_r$ 来算 $dis$,需要特判掉。
我们找每个 $\max(l,r)$ 的较大者,则对于每个 $u$,其只会于其编号两侧第一个 $d_v\le d_u$ 的位置产生支配对,则共产生 $O(sz)$ 个支配对。排序后用单调栈查找即可。
总支配对数 $O(n\log n)$,查找时间复杂度 $O(n\log^2 n)$。
我们不要求枚举所有真正不可被任意点对支配的点对,只需要枚举一个充分不必要的候选集。
将询问离线,和找出的支配对做一遍扫描线,用树状数组维护。
时间复杂度:$O(n\log^2 n+m\log n)$。
空间复杂度:$O(n\log n+m)$。
:::
:::success[Code]
```cpp
#include<bits/stdc++.h>
#define For(i,j,k) for(int i=(j);i<=(k);i++)
#define dFor(i,j,k) for(int i=(j);i>=(k);i--)
using namespace std;
#define MAXN 200005
#define MAXM 1000005
#define inf (1<<30)
#define INF (1ll<<60)
int n;
vector<pair<int,int>> e[MAXN];
bool vis[MAXN];
int sz[MAXN];
void dfs1(int u,int f){
sz[u]=1;
for(auto [v,w]:e[u]){
if(v==f||vis[v]) continue ;
dfs1(v,u);
sz[u]+=sz[v];
}
}
int all,Min,pos;
void dfs2(int u,int f){
int Max=all-sz[u];
for(auto [v,w]:e[u]){
if(v==f||vis[v]) continue ;
dfs2(v,u);
Max=max(Max,sz[v]);
}
if(Max<Min){
Min=Max;
pos=u;
}
}
vector<pair<int,long long>> a;
int c[MAXN],tot=0;
void dfs3(int u,int f,long long d){
c[u]=tot;
a.push_back({u,d});
for(auto [v,w]:e[u]){
if(v==f||vis[v]) continue ;
dfs3(v,u,d+w);
}
}
int st[MAXN],top=0;
struct Que{
int l,op;
long long id;
};
vector<Que> q[MAXN];
void find(){
sort(a.begin(),a.end());
top=0;
For(i,0,a.size()-1){
while(top&&a[st[top]].second>a[i].second) top--;
if(top){
int l=a[st[top]].first,r=a[i].first;
if(c[l]!=c[r]){
long long d=a[st[top]].second+a[i].second;
q[r].push_back({l,0,d});
}
}
st[++top]=i;
}
top=0;
dFor(i,a.size()-1,0){
while(top&&a[st[top]].second>a[i].second) top--;
if(top){
int l=a[i].first,r=a[st[top]].first;
if(c[l]!=c[r]){
long long d=a[i].second+a[st[top]].second;
q[r].push_back({l,0,d});
}
}
st[++top]=i;
}
}
void solve(int u){
dfs1(u,0);
all=sz[u];Min=inf;
dfs2(u,0);
u=pos;
a.clear();
tot=0;
c[u]=0;
a.push_back({u,0});
for(auto [v,w]:e[u]){
if(vis[v]) continue ;
tot++;
dfs3(v,u,w);
}
find();
vis[u]=1;
for(auto [v,w]:e[u]){
if(vis[v]) continue ;
solve(v);
}
}
int m;
long long ans[MAXM];
long long tr[MAXN];
void insert(int x,long long k){
while(x){
tr[x]=min(tr[x],k);
x-=x&-x;
}
}
long long query(int x){
long long ans=INF;
while(x<=n){
ans=min(ans,tr[x]);
x+=x&-x;
}
return ans;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n;
For(i,1,n-1){
int u,v,w;
cin>>u>>v>>w;
e[u].push_back({v,w});
e[v].push_back({u,w});
}
solve(1);
cin>>m;
For(i,1,m){
int l,r;
cin>>l>>r;
if(l==r){
ans[i]=-1;
continue ;
}
q[r].push_back({l,1,i});
}
For(i,1,n){
tr[i]=INF;
}
For(r,1,n){
for(auto [l,op,id]:q[r]){
if(op==0){
insert(l,id);
}else{
ans[id]=query(l);
}
}
}
For(i,1,m){
cout<<ans[i]<<'\n';
}
return 0;
}
```
:::