GYM100376F.Circle and Trees(树形DP+倍增LCA)

题意:

给你一棵树,请你为每个点确定一个圆(圆心坐标和半径)

这里要求:

圆心坐标和半径必须是整数,同时每个圆没有交点,同时每个点的圆必须包含它的子树的圆。

题解:

树形DP,DP的时候处理出每个叶子到根的路径上第一个有前驱兄弟的节点,这个叶子的圆心坐标确定了,它的祖先的坐标也就确定了。

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+100;
int n;
vector<int> g[maxn];
int tot=0;
int L[maxn],R[maxn];
int dfn[maxn];
int father[20][maxn];
int h[maxn];
int pre[maxn];//该节点到根的路径上第一个有前驱兄弟的点的前驱 
void dfs1 (int x,int xx) {
    int tt=0;
    pre[x]=xx;
    //printf("%d %d
",x,xx);
    for (int i=0;i<g[x].size();i++) {
        int y=g[x][i];
        if (y==father[0][x]) continue;
        father[0][y]=x;
        h[y]=h[x]+1;
        if (tt) {
            dfs1(y,tt);
        }
        else {
            dfs1(y,xx);
        }
        tt=y;
    }
}
int lca (int x,int y) {
    if (h[x]<h[y]) swap(x,y);
    for (int i=17;i>=0;i--) if (h[x]-h[y]>>i) x=father[i][x];
    if (x==y) return x;
    for (int i=17;i>=0;i--) {
        if (father[i][x]!=father[i][y]) {
            x=father[i][x];
            y=father[i][y];
        }
    }
    return father[0][x];
}
void dfs (int x) {
    tot++;
    dfn[tot]=x;
    int Min=1e6,Max=-1e6;
    if (g[x].size()==0) {
        if (pre[x]==0) {
            L[x]=-1e6+h[x];
            R[x]=L[x]+2;
            return;
        } 
        int lc=lca(x,pre[x]);
        L[x]=R[pre[x]]+1+h[x]-h[lc];
        R[x]=L[x]+2;
        return;
    }
    for (int y:g[x]) {
        dfs(y);
        Min=min(Min,L[y]);
        Max=max(Max,R[y]);
    }
    L[x]=Min-1;
    R[x]=Max+1;
    //printf("%d: %d %d
",x,L[x],R[x]);
}
int main () {
    scanf("%d",&n);
    int rt;
    for (int i=1;i<=n;i++) {
        int x;
        scanf("%d",&x);
        if (x==-1)
            rt=i;
        else
            g[x].push_back(i);
    }
    dfs1(rt,0);
    for (int i=1;i<=17;i++) for (int j=1;j<=n;j++) father[i][j]=father[i-1][father[i-1][j]];
    //for (int i=1;i<=n;i++) printf("%d ",pre[i]);
    //printf("
");
    dfs(rt);
    for (int i=1;i<=n;i++) printf("%d %d %d
",(L[i]+R[i])/2,0,(R[i]-L[i]+1)/2);
//    int f=1;
//    for (int i=1;i<=n;i++) if ((L[i]+R[i])%2) f=0;
//    for (int i=1;i<=n;i++) {
//        for (int j=1;j<=n;j++) {
//            if (L[i]>R[j]||R[i]<L[j]) continue;
//            f=0;
//        }
//    }
//    printf("%d
",f);
}
原文地址:https://www.cnblogs.com/zhanglichen/p/14585127.html