【CF566E】Restoring Map(构造)

点此看题面

  • 有一棵(n)个点的树,乱序给出与每个点距离小于等于(2)的点集。
  • 求构造一棵合法的树。
  • (nle10^3)

非叶节点间的连边

两个非叶节点(x,y)之间存在边,则对于它们两侧的两点(i,j),同时与(i,j)距离小于等于(2)的点只有(x,y)两点。

因此,非叶节点(x,y)之间有边的充要条件就是存在两个点集的交集恰好是({x,y})。要求这个,只需用枚举一对点集用(bitset)优化即可。

叶节点的连边

对于叶节点,显然它对应的点集是所有包含它的点集中最小的那个(可能有多个最小的,是和它同父节点的兄弟们,肯定都一样)。

而要找它的父节点,则它点集中所有的非叶节点应该恰好是它父节点的连边集合(加上父节点本身)。因此只要找到一个点的连边集合和修改后的点集相同,这个点就是它的父节点。

非叶节点很少时的判断

首先,如果没有非叶节点,则(n=2),直接判断即可。

其次,如果非叶节点数量为(1),那么这是一张菊花图,所有点的点集都是(1sim n),很容易判断出来,然后只要随便找个点做菊花图的中心即可。

再次,如果非叶节点数量为(2),先按照之前的方法求出这两个叶节点,那么点集可以被划分成两部分,随便分给这两个点即可。

代码:(O(frac{n^3}{32}))

#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 1000
using namespace std;
int n,p[N+5],w[N+5][N+5];bitset<N+5> s,S[N+5],E[N+5];
namespace FastIO
{
	#define FS 100000
	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
	#define pc(c) (FC==FE&&(clear(),0),*FC++=c)
	int OT;char oc,FI[FS],FO[FS],OS[FS],*FA=FI,*FB=FI,*FC=FO,*FE=FO+FS;
	I void clear() {fwrite(FO,1,FC-FO,stdout),FC=FO;}
	Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
	Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
	Tp I void write(Ty x) {W(OS[++OT]=x%10+48,x/=10);W(OT) pc(OS[OT--]);}
	Tp I void writeln(Con Ty& x,Con Ty& y) {write(x),pc(' '),write(y),pc('
');}
}using namespace FastIO;
int main()
{
	#define Exit return clear(),0
	RI i,j,x,y;for(read(n),i=1;i<=n;++i) for(read(x);x;--x) read(y),S[i].set(y);if(n==2) {writeln(1,n);Exit;}//特判无叶节点
	for(i=1;i<=n;++i) {for(j=1;j<=n&&S[i].test(j);++j);if(j<=n) break;}if(i>n) {for(i=2;i<=n;++i) writeln(1,i);Exit;}//特判一个叶节点
	RI t=0;for(i=1;i<=n;++i) for(j=i+1;j<=n;++j) (s=S[i]&S[j]).count()==2&&(x=s._Find_first(),//枚举每对点集求交
		y=s._Find_next(x),!w[x][y]&&(writeln(x,y),E[x].set(y),E[y].set(x),p[x]=p[y]=w[x][y]=w[y][x]=1,++t));//一对非叶节点间的连边
	if(t==1) {for(i=1;i<=n&&S[i].count()==n;++i);for(j=1;j<=n;++j) !p[j]&&(writeln(S[i].test(j)?x:y,j),0);Exit;}//特判两个叶节点
	for(i=1;i<=n;++i) p[i]&&(E[i].set(i),0);for(i=1;i<=n;++i) if(!p[i])//枚举每个非叶节点找父节点
	{
		for(t=0,j=1;j<=n;++j) S[j].test(i)&&(!t||S[j].count()<S[t].count())&&(t=j);//找所在最小点集
		for(s.reset(),j=1;j<=n;++j) S[t].test(j)&&p[j]&&(s.set(j),0);for(j=1;j<=n;++j) if(E[j]==s) {writeln(i,j);break;}//删去叶节点找父节点
	}return clear(),0;
}
败得义无反顾,弱得一无是处
原文地址:https://www.cnblogs.com/chenxiaoran666/p/CF566E.html