Loading

CF847L题解

在树上\(a,b\)两点存在边当且仅当包含\(b\)\(a\)集合大小与包含\(a\)\(b\)集合大小之和为\(n\)。因此直接暴力找出所有的边然后一堆判断是否是一棵树即可。

稍微讲一下怎么判:

  1. 首先边数不为\(n-1\)时显然不是一棵树。

  2. dfs一遍判断这是否是一棵树,具体为看一个点是否被多次访问到以及最后根节点子树和应该为\(n\)

  3. 最后暴力判断假设某个点为根,那么子树的情况是否与输入时相同。本人是直接每个点开了vector<vector<int>>记录子树内的情况,然后每次sort一遍方便判断两个集合是否相等。当然这也可以用hash做到。

int n,m;
vector<vector<int>>t[1111];
vector<pii>ans;
vector<int>e[1111];
int cnt[1111][1111],cnte;
int vis[1111],siz[1111],fa[1111];
void dfs1(int u)
{
    siz[u]=1;
    vis[u]=1;
    for(int v:e[u]) if(v^fa[u])
    {
        if(vis[v]) evil();
        fa[v]=u;
        dfs1(v);
        siz[u]+=siz[v];
    }
}
void dfs2(int u,int f,vector<int>&chi)
{
    chi.pb(u);
    for(int v:e[u]) if(v^f) dfs2(v,u,chi); 
}
void check()
{
    R(u,1,n)
    {
        vector<vector<int>>all;
        for(int v:e[u])
        {
            all.pb(vector<int>());
            dfs2(v,u,all.back());
            sort(all.back().begin(),all.back().end());
        }
        sort(all.begin(),all.end());
        if(t[u]!=all) evil();
    }   
}
signed main()
{
    scanf("%d",&n);
    R(i,1,n)
    {
        int del=n-1;
        while(del)
        {
            t[i].pb(vector<int>());
            scanf("%d:",&m);
            int x;
            R(j,1,m)
            {
                scanf("%d",&x);
                t[i].back().pb(x);
                if(j+1<=m) scanf(",");
                cnt[i][x]=m;
            }
            sort(t[i].back().begin(),t[i].back().end());
            del-=m;
            if(del) scanf("-");
        }
        sort(t[i].begin(),t[i].end());
    }
    R(u,1,n) R(v,u+1,n) if(cnt[u][v]+cnt[v][u]==n) e[u].pb(v),e[v].pb(u),++cnte,ans.pb(mkp(u,v));
    if(cnte^(n-1)) evil();
    dfs1(1);
    if(siz[1]^n) evil();
    check();
    writeln((int)ans.size());
    for(auto v:ans) writesp(v.fi),writeln(v.se);
}

posted @ 2021-06-29 09:48  yoisaki_hizeci  阅读(43)  评论(0)    收藏  举报