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

2 篇文章 0 订阅
1 篇文章 0 订阅

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的标签也是应该好好搞一下了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值