题目描述
有一棵大树.
已知 a , b , c a,b,c a,b,c,你需要找出 X , Y , Z X,Y,Z X,Y,Z,满足 d i s ( X , Y ) = a , d i s ( X , Z ) = b , d i s ( Y , Z ) = c dis(X,Y)=a,\ dis(X,Z)=b,\ dis(Y,Z)=c dis(X,Y)=a, dis(X,Z)=b, dis(Y,Z)=c.
数据范围
3 ≤ n , Q ≤ 2 × 1 0 5 3\le n,Q\le 2\times 10^5 3≤n,Q≤2×105, 1 ≤ a , b , c < n 1\le a,b,c<n 1≤a,b,c<n
题解
考虑一下到这三条路径会交于一个点 A A A ,而 A A A 到 X , Y , Z X,Y,Z X,Y,Z的路径长度可以先算出来,设为 x , y , z x,y,z x,y,z 且 x ≥ y ≥ z x \ge y \ge z x≥y≥z
我们发现如果一个点能够成为这个点 A A A,则需要满足它的三长链分别不小于 x , y , z x,y,z x,y,z ,即如果三长链长度为 x ′ ≥ y ′ ≥ z ′ x' \ge y' \ge z' x′≥y′≥z′ ,则 x ≤ x ′ , y ≤ y ′ , z ≤ z ′ x \le x',y \le y',z \le z' x≤x′,y≤y′,z≤z′
所以我们可以利用 d p dp dp 换根把每个点的三长链及其对应的叶子结点求出来,然后就是个经典的三维偏序问题啦,可以直接用树状数组解决
效率: O ( n l o g n ) O(nlogn) O(nlogn)
代码
#include <bits/stdc++.h>
#define P pair<int,int>
#define _(d) while(d(isdigit(c=getchar())))
using namespace std;const int N=2e5+5,Z=N<<1;
int R(){char c;_(!);int x=c^48;_()x=(x<<3)+(x<<1)+(c^48);return x;}
int a[N],b[N],c[N],s[N],Q,n,hd[N],V[Z],nx[Z],t,fa[N][20],dp[N],f[N][3],g[N][3],d[N][3],w[4],k[4],e[N],F[Z][22],Lg[Z],in[N],C;
P ax[N];struct O{int a,b,c,x,g;}p[Z],h[N];
bool cmp(O A,O B){
if (A.a==B.a){
if (A.b==B.b){
if (A.c==B.c) return A.g>B.g;
return A.c>B.c;
}
return A.b>B.b;
}
return A.a>B.a;
}
void add(int u,int v){
nx[++t]=hd[u];V[hd[u]=t]=v;in[v]++;
}
void dfs(int x,int fr){
fa[x][0]=fr;dp[x]=dp[fr]+1;
F[++C][0]=x;e[x]=C;
for (int i=1;fa[fa[x][i-1]][i-1];i++)
fa[x][i]=fa[fa[x][i-1]][i-1];
for (int i=hd[x];i;i=nx[i])
if (V[i]!=fr) dfs(V[i],x),F[++C][0]=x;
}
void work(int x,int j,int v){
for (int i=0;i<3;i++)
if (f[x][j]+1>f[v][i]){
for (int s=2;s>i;s--)
f[v][s]=f[v][s-1],d[v][s]=d[v][s-1],g[v][s]=g[v][s-1];
f[v][i]=f[x][j]+1;d[v][i]=d[x][j];g[v][i]=x;break;
}
}
void dp1(int x,int fr){
f[x][0]=0;f[x][1]=f[x][2]=-1;d[x][0]=x;
for (int v,i=hd[x];i;i=nx[i])
if ((v=V[i])!=fr)
dp1(v,x),work(v,0,x);
}
void dp2(int x,int fr){
if (in[x]>1) p[++t]=(O){f[x][0],f[x][1],f[x][2],x,1};
for (int v,i=hd[x];i;i=nx[i])
if ((v=V[i])!=fr) work(x,g[x][0]==v,v),dp2(v,x);
}
void update(int x,P v){
for (;x;x-=x&-x) ax[x]=max(ax[x],v);
}
P query(int x){
P A=ax[x];
for (;x<=n;x+=x&-x) A=max(A,ax[x]);
return A;
}
int Min(int x,int y){return dp[x]<dp[y]?x:y;}
int lca(int l,int r){
if (l>r) swap(l,r);int i=Lg[r-l+1];
return Min(F[l][i],F[r-(1<<i)+1][i]);
}
int dis(int u,int v){
int l=lca(e[u],e[v]);
return dp[u]+dp[v]-(dp[l]<<1);
}
int find(int u,int y,int ds){
int v=d[u][y],l=lca(e[u],e[v]);
if (dp[u]-dp[l]<ds){
ds-=dp[u]-dp[l];
ds=dp[v]-dp[l]-ds;u=v;
}
for (int i=0;ds;ds>>=1,i++)
if (ds&1) u=fa[u][i];
return u;
}
int main(){
n=R();for (int u,v,i=1;i<n;i++)
u=R()+1,v=R()+1,add(u,v),add(v,u);
dfs(1,0);t=0;dp1(1,0);dp2(1,0);Q=R();
for (int i=2;i<=C;i++) Lg[i]=Lg[i>>1]+1;
for (int i=C;i;i--)
for (int j=1;i+(1<<j)<=C+1;j++)
F[i][j]=Min(F[i][j-1],F[i+(1<<(j-1))][j-1]);
for (int x,y,z,i=1;i<=Q;i++){
a[i]=R();b[i]=R();c[i]=R();x=a[i]+b[i]-c[i];
y=a[i]+c[i]-b[i];z=b[i]+c[i]-a[i];
if (x>=0 && y>=0 && z>=0 && !((x&1)&(y&1)&(z&1))){
x>>=1;y>>=1;z>>=1;if (x<y) swap(x,y);
if (x<z) swap(x,z);if (y<z) swap(y,z);
h[i]=p[++t]=(O){x,y,z,i,0};
}
}
sort(p+1,p+t+1,cmp);
for (int i=1;i<=t;i++)
if (p[i].g) update(p[i].b,make_pair(p[i].c,p[i].x));
else{
P A=query(p[i].b);
if (A.second && A.first>=p[i].c) s[p[i].x]=A.second;
}
for (int i=1;i<=Q;i++)
if (s[i]){
w[k[1]=1]=find(s[i],0,h[i].a);
w[k[2]=2]=find(s[i],1,h[i].b);
w[k[3]=3]=find(s[i],2,h[i].c);
do{
if (dis(w[k[1]],w[k[2]])==a[i] && dis(w[k[1]],w[k[3]])==b[i] && dis(w[k[2]],w[k[3]])==c[i]){
printf("%d %d %d\n",w[k[1]]-1,w[k[2]]-1,w[k[3]]-1);break;
}
}while(next_permutation(k+1,k+4));
}
else puts("-1");
return 0;
}