NOIP模拟赛 遗失的二叉树

MZOJ

类似于区间动归
中序遍历:先遍历左儿子,再遍历根节点,最后遍历右儿子

盗用百科一张图
在这里插入图片描述

中序遍历的结果:

D − B − E − A − C − F D-B-E-A-C-F DBEACF

本题思路:
如能够构成一棵中序遍历的树,则在序列中一定存在一个 i i i的前面数是 i i i的左儿子, i i i后面的数构成 i i i的右儿子

举例:在上述 D − B − E − A − C − F D-B-E-A-C-F DBEACF中,若 A A A为根,则 A A A的前面 D , B , E D,B,E D,B,E构成 A A A的左儿子, A A A的后面构成右儿子

所以 l [ i ] [ j ] l[i][j] l[i][j]表示在区间 i i i j j j中,表示区间 [ i , j ] [i,j] [i,j] 是否可以作为 j j j的左儿子,即 l [ i ] [ j ] l[i][j] l[i][j]表示以 j j j为根 [ i , j − 1 ] [i,j-1] [i,j1]为左子树是否合法。 r [ i ] [ j ] r[i][j] r[i][j]表示在区间 [ i , j ] [i,j] [i,j]是否可以作为 i i i的右儿子,即 r [ i ] [ j ] r[i][j] r[i][j]表示以 i i i为根 [ i + 1 , j ] [i+1,j] [i+1,j]为右子树是否合法

b i a n [ i ] [ j ] bian[i][j] bian[i][j]表示 i , j i,j i,j两个点是否能相连,因为不能互质

枚举每个转移点,方程:

			if(bian[k][j] && l[i][k] && r[k][j-1]) 
				l[i][j]=true;
			if(bian[k][i] && l[i+1][k] && r[k][j])
				r[i][j]=true;

Code

#include<bits/stdc++.h>
#define ll long long
#define rep(i,a,b) for(register int (i)=(a);(i)<=(b);(i)++)
#define don(i,a,b) for(register int (i)=(a);(i)>=(b);(i)--)
using namespace std;
const int maxn=1e5+10;
const int maxm=1e3+10;
int T,n;
int a[maxn];
int l[maxm][maxm],r[maxm][maxm],bian[maxm][maxm];

template <class t> inline void read(t &x) {
	x=0;int f=1;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}
	while(isdigit(ch)){x=10*x+ch-'0';ch=getchar();}
	x*=f;
}

void work() {
	rep(len,2,n) {
		rep(i,1,n-len+1) {
			int j=i+len-1;
			rep(k,i,j) {
				if(bian[k][j] && l[i][k] && r[k][j-1]) 
				l[i][j]=true;
				if(bian[k][i] && l[i+1][k] && r[k][j])
				r[i][j]=true;
			}
		}
	}
}

inline int gcd(int a,int b) {
	if(b==0) return a;
	else gcd(b,a%b);
}

void readdata() {
	read(T);
	while(T--) {
		read(n);
		memset(a,false,sizeof(a));
		memset(bian,false,sizeof(bian));
		memset(l,false,sizeof(l));
		memset(r,false,sizeof(r));
		rep(i,1,n) read(a[i]);
		rep(i,1,n)
		 rep(j,i+1,n) {
		 	//if(!gcd(a[i],a[j])) bian[i][j]=bian[j][i]=true;
		 	 bian[i][j] = bian[j][i] = (gcd(a[i], a[j]) != 1);
		 }
		rep(i,1,n) l[i][i]=r[i][i]=true;
		work();
		bool flag=0;
		rep(i,1,n) {
			if(l[1][i] && r[i][n]) {
				flag=true;
				break;
			}
		}
		if(flag) printf("Yes\n");
		else printf("No\n");
	}
}

int main() {
	freopen("input.txt","r",stdin);
	readdata();
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值