题解:[APIO2007]风铃

传送门

你需要选一个满足下面两个条件的风铃:
(1) 所有的玩具都在同一层(也就是说,每个玩具到天花板之间的杆的个数是一样的)或至多相差一层。
(2) 对于两个相差一层的玩具,左边的玩具比右边的玩具要更靠下一点。
风铃可以按照下面的规则重新排列:任选一根杆,将杆两头的线“交换”。也就是解开一根杆左右两头的线,然后将它们绑到杆的另一头。这个操作不会改变更下面的杆上线的排列顺序。

其实看着这个题目我第一反应是平衡树,像splay,fhq之类的可以完成的区间翻转操作,但是这个题目不同的是,他改变两个之后其内部的顺序不变,所以我们可以感性的分析出来反转的顺序对答案没有太多的影响
分析:
由于最大最小相差深度小于等于1,所以可以先dfs一遍,特判一下,0输出0,大于1就是-1(考试时候忘了特判就挂了),对于等于1情况,我们来分析一下什么时候需要翻转:

  • 左小右大
  • 左有小有大,右大
  • 左小,右有小有大

所以,对于这个需要再来一次dfs,用0表示小,1表示大,2表示有小有大,遇到上面三种情况就ans++
完整代码如下:

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
using namespace std;
#define re register
#define gc getchar()
#define ll long long
#define il inline
const int N= 100010;
il int read() {
	re int x(0),f(1);
	re char ch=gc;
	while(ch<'0'||ch>'9') {
		if(ch=='-') f=-1;
		ch=gc;
	}
	while(ch>='0'&&ch<='9') {
		x=(x<<1)+(x<<3)+(ch^48);
		ch=gc;
	}
	return x*f;
}
int ls[N],rs[N],_max=-1,_min=1e8,deep[N],n,ans,flag=1;

void dfs1(int u) {
	if(ls[u]) {
		deep[ls[u]]=deep[u]+1;
		dfs1(ls[u]);
	} else {
		_min=min(_min,deep[u]+1);
		_max=max(_max,deep[u]+1);
	}
	if(rs[u]) {
		deep[rs[u]]=deep[u]+1;
		dfs1(rs[u]);
	} else {
		_min=min(_min,deep[u]+1);
		_max=max(_max,deep[u]+1);
	}
}

namespace SP1 {
	int x[3][3]= {
		0,2,2,
		2,1,2,
		2,2,2,
	};
	int dfs(int u,int dep) {
		if(u==-1) {
			if(dep==_max-1) return 1;
			return 0;
		}
		int a=dfs(ls[u],dep+1);
		int b=dfs(rs[u],dep+1);
		if(a==0&&b==1) ++ans;
		if(a==2&&b==1) ++ans;
		if(a==0&&b==2) ++ans;
		if(a==2&&b==2) flag=0;
		return x[a][b];
	}
	int main() {
		dfs(1,0);
		if(!flag) {
			cout<<-1<<endl;
			return 0;
		}
		cout<<ans<<endl;
	}
};
int main() {
	
	n=read();
	for(int i=1; i<=n; ++i)
		ls[i]=read(),rs[i]=read();
	dfs1(1);
	if(_max-_min==1) {
		SP1::main();
		return 0;
	}
	if(_max==_min) cout<<0;
	cout<<-1;

	return 0;
}

这个题目的空间虽然只有16M,但完全是够得

猜你喜欢

转载自blog.csdn.net/weixin_43464026/article/details/87357647
今日推荐