ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【洛谷6776】[NOI2020] 超现实树(思维)

2020-12-18 19:03:57  阅读:196  来源: 互联网

标签:洛谷 LC 6776 rc lc NOI2020 && RC define


点此看题面

  • 给定一个由\(m\)棵无标号、区分左右儿子的二叉树组成的集合。
  • 规定一棵树\(T\)能变成\(T'\),当且仅当它能通过若干次操作,每次把一个叶节点替换成一棵任意形状的非空二叉树,最终得到\(T'\)。
  • 要求判断是否只有有限种二叉树是无法通过这个集合中的树变得的。
  • 数据组数\(\le100,\sum n,\sum m\le2\times 10^6\)

怎么说呢,这种题目就是做的时候非常难想,而一看题解却又对着结论直呼傻逼。。。

想不到给它加什么标签了,因此变硬凑了个“思维”的标签上去。

一个递归的思路

设\(f(V,x)\)表示对于集合\(V\)中的所有树,它们以\(x\)为根的子树是否几乎完备。那么我们要求的实际上就是\(f(T,1)\)。

首先,如果在集合\(V\)中存在一棵树,满足它的点\(x\)是叶节点,显然它必然完备了。

否则,我们考虑什么样的情况下多棵树的组合才能让它几乎完备。

发现以\(x\)为根的树有四种:单独一个点、没有左儿子的、没有右儿子的、左右儿子都有的。

其中第一种树显然是无法得到的,但因为它只有这么一种,是有限的,所以无法得到也没关系。

接下来就对于剩余三种分类讨论一下。

没有左儿子的/没有右儿子的

以没有左儿子的情况为例,那么我们需要先找出\(V\)中所有没有左儿子的树的集合\(V_1\),然后其实就是要判断它们的右子树是否几乎完备,即\(f(V_1,rc)\)是否为\(1\)。

而没有右儿子的情况,相应的就是找出所有没有右儿子的树的集合\(V_2\),然后判断\(f(V_2,lc)\)是否为\(1\)。

注意,只要\(f(V_1,rc)=1\)和\(f(V_2,lc)=1\)中有一个不满足,无法得到的树就有无限种。

左右儿子都有的

首先,如果前面的两种都满足了,而在\(V\)中剩下那些左右儿子都有的树的集合中存在某一棵树满足它的左右儿子都是叶节点,那么就能证明\(f(V,x)=1\)了,这应该是显然的。

接下来,我们先证明,对于一棵左右儿子都不是叶节点的树,它是没用的:

  • 如果它能由\(V\)中的其他树变成:显然它是没用的。
  • 如果它不能由\(V\)中的其他树变成:考虑把它的左子树换成单独一个点,得到的新树仍然不可能由其他树变成(否则就能由其他树先变成这棵新树,再变成当前树),而且也不可能由当前树变成。这样一来,我们有无限种方式在这棵新树的右子树中加点,使得得到的树无法生成。也就是说,不管是否有当前树,这个集合都是不完备的,那么当前树显然没用了。

因此,我们只要求出所有左儿子是叶节点的树的集合\(V_3\)和所有右儿子是叶节点的树的集合\(V_4\),判断是否满足\(f(V_3,rc)=f(V_4,lc)=1\)即可。

时间复杂度

由于\(V_1,V_2,V_3,V_4\)无交,对于一棵树我们访问的次数不会超过它的高度,更不会超过它的点数,肯定正确。

注意,具体实现和题解中的说法可能有些差异。我们不必统一每棵树中的节点编号,只要知道当前点对应到集合中每棵树的哪个点即可。

代码:\(O(\sum n)\)

#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 2000000
#define pb push_back
using namespace std;
int m,n[N+5],nw[N+5];vector<int> lc[N+5],rc[N+5];
struct node {int p,x;I node(CI a=0,CI b=0):p(a),x(b){}};vector<node> V[N+5];
class FastIO
{
	private:
		#define FS 100000
		#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
		#define D isdigit(c=tc())
		char c,*A,*B,FI[FS];
	public:
		I FastIO() {A=B=FI;}
		Tp I void read(Ty& x) {x=0;W(!D);W(x=(x<<3)+(x<<1)+(c&15),D);}
}F;
vector<node>::iterator it;I bool Check(CI T)//检验V[T]集合
{
	#define REP for(it=V[T].begin();it!=V[T].end();++it)
	#define LC lc[it->p][it->x]//左儿子
	#define RC rc[it->p][it->x]//右儿子
	#define Exit(v) return V[T].clear(),v//清空vector并返回v
	RI nw=T;if(V[T].empty()) return 0;REP if(!LC&&!RC) Exit(1);//如果为空返回0,如果是叶节点返回1
	REP !LC&&(V[T+1].pb(node(it->p,RC)),0);if(!Check(T+1)) Exit(0);//如果f(V1,rc)=0返回0
	REP !RC&&(V[T+1].pb(node(it->p,LC)),0);if(!Check(T+1)) Exit(0);//如果f(V2,kc)=0返回0
	REP if(LC&&RC&&!lc[it->p][LC]&&!rc[it->p][LC]&&!lc[it->p][RC]&&!rc[it->p][RC]) Exit(1);//如果存在左右儿子都是叶节点的点返回1
	REP LC&&RC&&!lc[it->p][LC]&&!rc[it->p][LC]&&(V[T+1].pb(node(it->p,RC)),0);if(!Check(T+1)) Exit(0);//如果f(V3,rc)=0返回0
	REP LC&&RC&&!lc[it->p][RC]&&!rc[it->p][RC]&&(V[T+1].pb(node(it->p,LC)),0);Exit(Check(T+1));//返回f(V4,lc)
}
int main()
{
	RI Tt,i,j,x,y;F.read(Tt);W(Tt--)
	{
		for(F.read(m),i=1;i<=m;++i) for(F.read(n[i]),V[0].pb(node(i,1)),
			lc[i].pb(0),rc[i].pb(0),j=1;j<=n[i];++j) F.read(x),F.read(y),lc[i].pb(x),rc[i].pb(y);//存储一棵树的信息
		puts(Check(0)?"Almost Complete":"No");//验证对于整个集合,以1为根的树是否完备
		for(i=1;i<=m;++i) lc[i].clear(),rc[i].clear();//清空
	}return 0;
}

标签:洛谷,LC,6776,rc,lc,NOI2020,&&,RC,define
来源: https://www.cnblogs.com/chenxiaoran666/p/Luogu6776.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有