CodeForces 765E. Tree Folding

来源:互联网 发布:风险评价矩阵法 编辑:程序博客网 时间:2024/05/22 06:33

题面:

Tree Folding

time limit per test : 2 seconds memory limit per test : 512 megabytes input : standard input output :standard output

Vanya wants to minimize a tree. He can perform the following operation multiple times: choose a vertex v, and two disjoint (except for v) paths of equal length a0 = v, a1, …, ak, and b0 = v, b1, …, bk. Additionally, vertices a1, …, ak, b1, …, bk must not have any neighbours in the tree other than adjacent vertices of corresponding paths. After that, one of the paths may be merged into the other, that is, the vertices b1, …, bk can be effectively erased:
Help Vanya determine if it possible to make the tree into a path via a sequence of described operations, and if the answer is positive, also determine the shortest length of such path.
Input
The first line of input contains the number of vertices n (2 ≤ n ≤ 2·105).
Next n - 1 lines describe edges of the tree. Each of these lines contains two space-separated integers u and v (1 ≤ u, v ≤ n, u ≠ v) — indices of endpoints of the corresponding edge. It is guaranteed that the given graph is a tree.
Output
If it is impossible to obtain a path, print -1. Otherwise, print the minimum number of edges in a possible path.

题意:

可以对一颗树进行选取某一个地方作为中点,合并两条完全不重合且长度相同的链,直到这个树最后成为一条链。问这条链按照这样操作最后最短长度是?如果这棵树不能成为一条链,输出 -1.

思考:

按照样例给的树,加上随手画几棵树,可以发现,这个树最后能够合并成一条链,那他的根就是他原来形状直径(树上最长的一条链)的中点。知道根后,就好做呢,从根出发,判断每个子节点出发的链能不能合并的的条件就是这些子节点深度有没有一样。这个过程一个dfs就可以搞定。
先从根节点开始dfs。
对于每一个节点,用一个set记录:以该点为根的子树的深度。
a) 如果此节点的某个子节点个数大于2表示无法满足最后合成一条链,直接返回-1.
b) 若set的元素个数<=1,那么,以该点为根的子树,显然是可以缩成一条链,且该点为链的端点。
c) 若set元素个数=2,只有当该点为根才满足成为一条链。
d)最后注意如果链长度为偶数,注定还可以合并。

#include<bits/stdc++.h>using namespace std;template<int N,int M>//N点的个数,M边的个数struct Graph{    int top;    struct Vertex{        int head;    }V[N];    struct Edge{        int v,next;    }E[M];    void init(){        memset(V,-1,sizeof(V));        top = 0;    }    void add_edge(int u,int v){        E[top].v = v;        E[top].next = V[u].head;        V[u].head = top++;    }};const int N=2*1e5+10;Graph<N,N*2>g;int d[N],dl[N],dr[N];void dfs(int u,int f,int dep,int *d){   d[u]=dep;   for(int i=g.V[u].head;i!=-1;i=g.E[i].next){      int v=g.E[i].v;      if(v==f) continue;      dfs(v,u,dep+1,d);   }}int solve(int u,int f){    set<int>st;    for(int i=g.V[u].head;i!=-1;i=g.E[i].next){        int v=g.E[i].v;        if(v==f) continue;        int t=solve(v,u);        if(t==-1) return -1;        st.insert(t+1);    }    if(st.size()==0) return 0;    if(st.size()==1) return *st.begin();    if(st.size()==2&&f==0) return *st.rbegin()+*st.begin();    return -1;}int main(){    int n;    while(~scanf("%d",&n)){        g.init();        for(int i=1;i<n;i++){            int a,b;scanf("%d %d",&a,&b);            g.add_edge(a,b);            g.add_edge(b,a);        }        int l=1,r=1;//这段代码找树上直径以及中点        dfs(1,0,0,d);        for(int i=1;i<=n;i++) if(d[i]>d[l]) l=i;        dfs(l,0,0,dl);        for(int i=1;i<=n;i++) if(dl[i]>dl[r]) r=i;        dfs(r,0,0,dr);        int rt=l;        for(int i=1;i<=n;i++){//找到l,r两点之间的中点就是我们要dfs树的根            if(dl[i]+dr[i]==dl[r]&&min(dl[i],dr[i])>min(dl[rt],dr[rt])) rt=i;        }        int ans=solve(rt,0);        while(ans%2==0) ans/=2;        printf("%d\n",ans);    }}

其实如果没有发现根是树直径中点的规律,也没有关系,随便选一点,一直dfs只要出现该节点的深度个数为 2,并且还有父亲的情况下就以当前节点为根继续搜下去判断满不满足条件。详见以下代码。

#include<bits/stdc++.h>using namespace std;template<int N,int M>//N点的个数,M边的个数struct Graph{    int top;    struct Vertex{        int head;    }V[N];    struct Edge{        int v,next;    }E[M];    void init(){        memset(V,-1,sizeof(V));        top = 0;    }    void add_edge(int u,int v){        E[top].v = v;        E[top].next = V[u].head;        V[u].head = top++;    }};const int N=2*1e5+10;Graph<N,N*2>g;int root;int dfs(int u,int f){   set<int>st;   for(int i=g.V[u].head;i!=-1;i=g.E[i].next){        int v=g.E[i].v;        if(v==f) continue;        int t=dfs(v,u);        if(t==-1) return -1;        st.insert(t+1);    }    if(st.size()==0) return 0;    if(st.size()==1) return *st.begin();    if(st.size()==2&&f==0) return *st.rbegin()+*st.begin();    if(st.size()>=3) return-1;    root=u;    return -1;}int main(){     int n;     while(~scanf("%d",&n)){        g.init();        root=0;        for(int i=1;i<=n;i++){            int a,b;scanf("%d %d",&a,&b);            g.add_edge(a,b);            g.add_edge(b,a);        }        int ans=dfs(1,0);        if(ans==-1&&root) ans=dfs(root,0);        while(ans%2==0) ans/=2;        printf("%d\n",ans);     }}
0 0