CF847L题解
在树上\(a,b\)两点存在边当且仅当包含\(b\)的\(a\)集合大小与包含\(a\)的\(b\)集合大小之和为\(n\)。因此直接暴力找出所有的边然后一堆判断是否是一棵树即可。
稍微讲一下怎么判:
-
首先边数不为\(n-1\)时显然不是一棵树。
-
dfs一遍判断这是否是一棵树,具体为看一个点是否被多次访问到以及最后根节点子树和应该为\(n\) -
最后暴力判断假设某个点为根,那么子树的情况是否与输入时相同。本人是直接每个点开了
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);
}

浙公网安备 33010602011771号