P9058 [Ynoi2004] rpmtdq
前言
开个巨坑,可能以后会三四天一更?
分析
路径考虑点分治,以下的 \(dis\) 皆为确定根节点的。
以下点对 \((i,j)\) 都钦定 \(dis_i\le dis_j\)
注意到很多点对对答案是无用的,条件为若点对 \((i,j)\)存在 \(k\in[l+1,r-1]\),满足 \(dis_i>=dis_k\ or\ dis_j\ge dis_k\),则点对 \((i,j)\)无用,又称 \((i,k)\ or\ (k,j)\) 支配了 \((i,j)\)。
问题转化为了找到不被支配的点对 \((i,j)\),条件由定义得 \(max(dis_i,dis_j)<\min\limits_{k=l+1}^{r-1} dis_k\),但是点对数量为平方级别,支配点对常常与前后继有关,我们不妨大胆猜测,对于点 \(i\),只有 \(i\) 的前继与后继是有用的点对,小心求证一下。
令 \(pre\) 为 \(i\) 满足条件的前继,\(j\) 为小于 \(pre\) 满足条件的一点。
考虑反证法,若点对 \((j,i)\) 未被支配。
有 \(dis_j,dis_{pre}<dis_i\)。
\[dis_{j,pre}=dis_{j}+dis_{pre}<dis_j+dis_i=dis_{i,j}
\]
发现 \((i,j)\) 被 \((j,pre)\) 支配。
以上为前继证明,后继同理。
考虑代码实现。
由上述条件可以维护一个 \(dis\) 单调不升的栈,依次插入有序标号实现。
最后统计答案二维数点即可。
代码
const int N=2e5+10;
const int M=1e6+10;
int tot,len,top,rt,n,q,fa[N][20],sz[N],mx[N];
bool vis[N];
ll ans[M];
vector <PII> Q[N],G[N];
vector <int> Point[N];
pair<int,ll> vec[N],sta[N];
void getroot(int u,int par){
sz[u]=1,mx[u]=0;
for(auto [v,w]:G[u]){
if(v==par||vis[v])
continue;
getroot(v,u);
sz[u]+=sz[v],mx[u]=max(mx[u],sz[v]);
}
mx[u]=max(mx[u],tot-sz[u]);
if(mx[u]<mx[rt]) rt=u;
}
void add(int u,int par,ll dep){
vec[++len]={u,dep};
for(auto [v,w]:G[u]){
if(v==par||vis[v])
continue;
add(v,u,dep+w);
}
}
void dfs(int u){
vis[u]=1,len=0;
for(auto [v,w]:G[u]){
if(vis[v]) continue;
add(v,0,w);
}
vec[++len]={u,0};
sort(vec+1,vec+len+1);
top=0;
for(int i=1;i<=len;i++){
while(top&&sta[top].se>vec[i].se)
top--;
if(top) Point[sta[top].fi].pb(vec[i].fi);
sta[++top]=vec[i];
}
top=0;
for(int i=len;i>=1;i--){
while(top&&sta[top].se>vec[i].se)
top--;
if(top) Point[vec[i].fi].pb(sta[top].fi);
sta[++top]=vec[i];
}
for(auto [v,w]:G[u]){
if(vis[v]) continue;
tot=sz[v],rt=0;
getroot(v,0);
dfs(rt);
}
}
struct BIT{
ll t[N];
BIT(){memset(t,0x3f,sizeof t);}
void add(int x,ll v){
while(x<=n) t[x]=min(t[x],v),x+=(x&(-x));
}
ll query(int x){
ll res=0x3f3f3f3f3f3f3f3f;
while(x) res=min(res,t[x]),x-=(x&(-x));
return res;
}
}T;
struct LCA{
ll dis[N];
int dfn[N],mn[N][19],ti;
void dfs(int u,int par){
mn[(dfn[u]=++ti)][0]=par;
for(auto [v,w]:G[u])
if(v!=par)
dis[v]=dis[u]+w,dfs(v,u);
}
int MIN(int x,int y){
return dfn[x]<dfn[y]?x:y;
}
void init(){
for(int i=1;i<=__lg(n);i++){
for(int j=1;j+(1<<i)-1<=n;j++){
mn[j][i]=MIN(mn[j][i-1],mn[j+(1<<(i-1))][i-1]);
}
}
}
int getlca(int u,int v){
if(u==v) return u;
if((u=dfn[u])>(v=dfn[v])) swap(u,v);
int d=__lg(v-u++);
return MIN(mn[u][d],mn[v-(1<<d)+1][d]);
}
ll getdis(int u,int v){
return dis[u]+dis[v]-2*dis[getlca(u,v)];
}
}F;
void Main(){
n=rd;
for(int i=1;i<n;i++){
int u=rd,v=rd,w=rd;
G[u].pb({v,w});
G[v].pb({u,w});
}
tot=n,mx[0]=2e9;
getroot(1,0);
dfs(rt);
F.dfs(1,0),F.init();
q=rd;
for(int i=1;i<=q;i++){
int l=rd,r=rd;
Q[l].pb({r,i});
}
for(int i=n;i;i--){
for(int j:Point[i]) T.add(j,F.getdis(i,j));
for(auto [x,id]:Q[i])
ans[id]=T.query(x);
}
for(int i=1;i<=q;i++){
if(ans[i]==0x3f3f3f3f3f3f3f3f)
ans[i]=-1;
printf("%lld\n",ans[i]);
}
}