[NOI2013]树的计数(树的DFS,BFS序性质)

感觉对树的理解更深了点


首先想到对BFS序列进行划分,那么方便起见,将BFS序列改为1~n的顺序排列,对应地改变DFS序列 


对于同一种划分方案,有几棵满足DFS序的树呢?这相当于问子节点到父节点有几种连法。
在两种不同连法中,父1,父2,子 的相对顺序不同,所以只有一种符合DFS序。
也就是说:每种划分方案最多对应一棵树 

接下来需要检验一种划分方案是否可行 
每两次划分之间有一行深度相同的节点,所以从每行内部、行与行之间两个方面来考虑:
1. 设某一行为:l,l+1,…,r,只需满足DFS访问顺序是从左到右的,因此必须:pos[l]<pos[l+1]<…<pos[r] 其中 pos[i]:DFS序列中i出现的位置 
2. 1) BFS序列中,1与2之间必须划分 
   2) 设改变后的DFS序列为a[1~n],depth(x)表示结点x在树中的深度 
      a[i]要么没孩子 -> depth(a[i])>=depth(a[i+1]),要么最先访问的孩子为a[i+1] -> depth(a[i])+1==depth(a[i+1])
      所以对于任意i(1<=i<n), depth(a[i])+1>=depth(a[i+1])

有了判定方法,就可以统计方案数了 
设x[i]为1表示i与i+1之间有划分,为0表示不划分,上述条件转化为:
① x[1]=1
② if(pos[i]>pos[i+1]) x[i]=1
③ if(a[i]<a[i+1]) x[a[i]]+x[a[i]+1]+…+x[a[i+1]-1] <=1 (若a[i]>a[i+1],显然成立; a[i]与a[i+1]间最多划分1次,否则深度相差2以上)

可以用②来"切分"③,使③中某些式子的变量值唯一确定 

可以证明:③中那些还确定不了的式子一定只含一个未知数(a[j]==a[j+1]),这些x[i]既可为0又可为1,概率相等,给它们取值0.5就行了 

算法复杂度为线性,记录③中那些单变量的不等式值是被否②+③确定时,使用了打标记的技巧而避免了使用线段树 

ans=1+sigma(x[i])


代码和题解一样长。。。

#include<stdio.h>
#include<stdlib.h>
double x[200005]={0},s[200005]={0};
int t[200005]={0},a[200005]={0},pos[200005]={0},flag[200005]={0},sta[200005]={0};
int main()
{
	double ans=1.0;
	int n,i,j,p=0;
	scanf("%d",&n);
	for(i=1;i<=n;i++)
	{
		scanf("%d",&j);
		t[j]=i;//j在原DFS序的第i位上 
	}
	for(i=1;i<=n;i++)
	{
		scanf("%d",&j);
		a[t[j]]=i;
		pos[i]=t[j];
	}
	x[1]=1.0;
	for(i=1;i<n;i++)
	{
		if(pos[i]>pos[i+1]) x[i]=1.0;
		if(x[i]==1.0)
		{
			flag[i]++;
			flag[i+1]--;
		}
		s[i]=s[i-1]+x[i];
	}
	for(i=1;i<n;i++)
		if(a[i]<a[i+1])
		{
			if(s[a[i+1]-1]-s[a[i]-1]>0)
			{
				flag[a[i]]++;
				flag[a[i+1]]--;
			}
			else sta[++p]=a[i];
		}
	for(i=1;i<=n;i++)
		flag[i]+=flag[i-1];
	for(i=1;i<=p;i++)
		if(flag[sta[i]]==0) x[sta[i]]=0.5;//flag[xi]==1意味着xi的值已经被确定 
	for(i=1;i<=n;i++)
		ans+=x[i];
	printf("%.3lf",ans);
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值