#4509. Three points 2

题目描述

有一棵大树.

已知 $a,b,c$,你需要找出 $X,Y,Z$,满足 $dis(X,Y)=a,\ dis(X,Z)=b,\ dis(Y,Z)=c$.

数据范围

$3\le n,Q\le 2\times 10^5$,$1\le a,b,c

题解

考虑一下到这三条路径会交于一个点 $A$ ,而 $A$ 到 $X,Y,Z$的路径长度可以先算出来,设为 $x,y,z$ 且 $x \ge y \ge z$

我们发现如果一个点能够成为这个点 $A$,则需要满足它的三长链分别不小于 $x,y,z$ ,即如果三长链长度为 $x' \ge y' \ge z'$ ,则 $x \le x',y \le y',z \le z'$

所以我们可以利用 $dp$ 换根把每个点的三长链及其对应的叶子结点求出来,然后就是个经典的三维偏序问题啦,可以直接用树状数组解决

效率: $O(nlogn)$

代码

#include 
#define P pair
#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]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<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)
        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<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 (xif (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;
}

 

你可能感兴趣的:(#4509. Three points 2)