Renatus的博客

不管到了什么时候,人们最缺少的都是时间

Codeforces 765E. Tree Folding 题目详解+错点记录

Codeforces 765E


RT,这是一道题。。。其实我也不知道为什么就A了

Codeforces 765E. Tree Folding

Problem

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.

Examples

Input

6
1 2
2 3
2 4
4 5
1 6

Output

3

Input

7
1 2
1 3
3 4
1 5
5 6
6 7

Output

-1

题目分析

最小长度的含义

说一下分析吧。这题说实话我是在乱搞,看下代码就知道了。。。
本题很显然是道DFS题,题意大概就是把长度相同的链合并去重,最后问你这条链是否存在,如果存在最小长度为多少。我一开始还挺奇怪,如果它形成一条链,那么长度不就固定了么?然而我发现最后这句话的意思,不过是让你把偶数长度的链子不断折叠,直到它成为一条长度为奇数的链子。。。这我也是很无语

算法思路 I

我个人想法经历了许多变化,一开始我打算从叶子开始搜索,一次性地将树合并完成,然而事实证明这样做要维护的信息太多,因为有可能搜到的某个节点之前发现的链子可以与当前的合并,而这一点很难维护与处理(我当时甚至让DFS函数的返回值为一个 set<int>,我是不是疯了。。。),我们还需要新的方法。

算法思路 II

后来,我发现,我们可以用很多轮DFS来进行合并,每次从儿子数最多的节点开始,并不会超时。这是因为,我们每一轮合并后,已经把能找到的最短的链子都找完了(即全都合并完了)。比如第一轮,我们从某个节点开始搜索,我们如果发现了可以合并的链子,立刻就合并掉。这样,长度为1的链子不会因为它旁边没有合并出另一条长度为1的链子而等到下一轮(因为如果是这样,他旁边的链子就应该已经合并出来了)。那么下一轮最短的链子长度就至少为2了。每次合并后,我们都只是合并了支路,其实并不影响干线,所以第二轮同理,到第三轮链子长度便最短为3了。这样,最短的链子长度逐渐递增,可知这样的最坏复杂度为O(n32),当然这种情况(第i轮只合并一条链子,且其长度为i)很好构造,如n=2105时实际并不容易过,然而可能没有像我这种乱搞的,所以并没有考虑到那样的数据。这样,我们就用多轮DFS成功地解决了这个问题O(∩_∩)O~(感觉这样做像brute force方法。。。)

总结

当然,还有许许多多的细节,而本人选择在DFS中使用 set 大法进行乱搞,删点(即合并链子)并记录,最后输出时还有两大陷阱,一个是上文提到过的输出不断要除2,另一个就是防止将删掉的链子当做答案输出。

代码

下面贴上本人丑陋的代码:

#include<set>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<stack>
#define maxn 200005
using namespace std;
int n,root;
set<int> geo[maxn];
int vis[maxn],exi[maxn],size[maxn];
int f=1,cnt=0,cha=0;
/*set<int> DFS(int x,int depth){
    if(geo[x].size()==1&&x!=root){
        set<int> cal;
        cal.insert(0);
        return cal;
    }
    vis[x]=true;
    set<int> cal,k;
    for(int i=0;i<geo[x].size();i++){
        if(vis[geo[x][i]])continue;
        k=DFS(geo[x][i],depth+1);
        if(k.size()>2||f==-1){f=-1;k.clear();return k;}
        for(set<int>::iterator it=k.begin();it!=k.end();it++){
            if(*it+1!=depth)
                cal.insert(*it+1);
        }
        if(cal.size()>2||f==-1){f=-1;cal.clear();return cal;}
    }
    return cal;
}*/
int DFS(int x){
    vis[x]=cnt;
    set<int> cal;
    stack<int> ans;
    int f=0;
    for(set<int>::iterator it=geo[x].begin();it!=geo[x].end();it++){
        if(vis[*it]==cnt||exi[*it]==0)continue;
        f=1;
        int k=DFS(*it);
        if(k==-1)continue;
        if(cal.count(k+1)){
            cha=1;
            exi[*it]=0;
            ans.push(*it);
            continue;
        }
        cal.insert(k+1);
    }
    while(!ans.empty()){
        geo[x].erase(ans.top());
        ans.pop();
    }
    if(f==0){
        return 0;
    }
    if(cal.size()==1){
        return *cal.begin();
    } 
    return -1;
}
int DFS2(int x){
    vis[x]=cnt;
    int k=0;
    for(set<int>::iterator it=geo[x].begin();it!=geo[x].end();it++){
        if(vis[*it]==cnt)continue;
        if(exi[*it]==0)return -1;
        k=DFS2(*it);
        if(k==-1)return -1;
    }
    return k+1;
}
int main(){
    /*freopen("input.txt","r",stdin);
    freopen("output.txt","w",stdout);*/
    scanf("%d",&n);
    int u,v;
    for(int i=0;i<n-1;i++){
        scanf("%d%d",&u,&v);
        geo[u].insert(v);
        geo[v].insert(u);
    }
    for(int i=1;i<=n;i++)
        exi[i]=1;
    for(;;){
        int max1=0,max2;
        for(int j=1;j<=n;j++){
            if(exi[j]==0)continue;
            int op=geo[j].size();
            if(max1<op){
                max1=op;
                max2=j;
            }
        }
        if(max1<=2){
            cnt++;
            for(int j=1;j<=n;j++){
                if(exi[j]==0||vis[j]==cnt||geo[j].size()!=1)continue;
                int out=DFS2(j);
                if(out!=-1){
                    out--;
                    while(out!=0&&out%2==0)out=out/2;
                    printf("%d",out);
                    return 0;
                }
            }
        }else{
            cnt++;
            DFS(max2);
            if(cha==0){
                printf("-1");
                return 0;
            }
        }
        cha=0;
    }
    /*set<int> k=DFS(root,0);
    if(k.size()>1||f==-1)
        printf("-1");
    else {  
        int sum=0;
        for(set<int>::iterator it=k.begin();it!=k.end();it++){
            sum+=*it;
        }
        printf("%d",sum);
    }*/
    return 0;
}

注释记录了本弱苦苦挣扎在各种思路上的痛苦QAQ

错点记录

  1. 思路有问题

  2. set 容器不能在迭代器访问时删点(所以我用stack保存一下要删的点)

  3. 未认真思考题意,如本题中的最短长度

  4. 未注意避免答案的干扰因素,如本题中将已删去的链当做答案输出

~说实话今天本来想练点DP的,然而并没有一道题是。。。(当然应该是我弱,想不到DP怎么做)CF的标签也是应该好好搞一下了

阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/Goseqh/article/details/56027660
文章标签: codeforces
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

不良信息举报

Codeforces 765E. Tree Folding 题目详解+错点记录

最多只允许输入30个字

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭