题面
BZOJ
这题有点假,
bzoj
b
z
o
j
上如果要交的话请输出
ans−0.001,ans,ans+0.001
a
n
s
−
0.001
,
a
n
s
,
a
n
s
+
0.001
题解
数的形态和编号没有关系,因此对于
bfs
b
f
s
序重标号,同时修改一下
dfs
d
f
s
序方便做题。
因为
bfs
b
f
s
的层数等于树高,所以我们相当于要把
bfs
b
f
s
划分为若干段,
那么,每一种
bfs
b
f
s
划分显然要么不合法,要么对应一种树的形态。
那么,我们来考虑
bfs
b
f
s
划分的几个限制。
首先有一个很明显的限制:
如果划分了一段
[L,R]
[
L
,
R
]
,那么
dfn[L]<dfn[L+1]<...<dfn[R]
d
f
n
[
L
]
<
d
f
n
[
L
+
1
]
<
.
.
.
<
d
f
n
[
R
]
原因很简单,因为一个点的所有儿子对应的出现顺序在
bfs
b
f
s
和
dfs
d
f
s
序中相同
显然所有儿子都是从左往右遍历,因此这一项显然成立。
令
pos
p
o
s
为
dfs
d
f
s
序的反数组,
考虑
dfs
d
f
s
序中相邻的两个元素
x,x+1
x
,
x
+
1
。
显然要么是父子关系,要么是兄弟关系,要么
x+1
x
+
1
是
x
x
祖先的儿子。
所以可以列出不等式
当然了,还有一个约束,根节点后面必须断开。
我们构建一个数组 s s ,的话就表示 i i 和必须断开
归类一下,约束条件有三项:
1.根节点后面必须断开,而根节点一定是
bfs
b
f
s
序中的
1
1
2.同一段中的所有点满足
dfs
d
f
s
序递增,即如果
pos[i]>pos[i+1]
p
o
s
[
i
]
>
p
o
s
[
i
+
1
]
,那么必须断开,也就是
s[i]=1
s
[
i
]
=
1
3.如果
dfn[i]<dfn[i+1]
d
f
n
[
i
]
<
d
f
n
[
i
+
1
]
,那么,
∑dfn[i+1]−1j=dfn[i]s[i]≤1
∑
j
=
d
f
n
[
i
]
d
f
n
[
i
+
1
]
−
1
s
[
i
]
≤
1
,这个式子在计算的时候需要满足
dfn[i]<dfn[i+1]
d
f
n
[
i
]
<
d
f
n
[
i
+
1
]
,而根据推导
dep[i+1]−dep[i]≤1
d
e
p
[
i
+
1
]
−
d
e
p
[
i
]
≤
1
。
如果知道了 s s ,那么就是树高,也就是 bfs b f s 序分开的段数。
考虑一下贡献是怎么产生的。
两个相邻的
bfs
b
f
s
序之间要么不能断开,贡献为
0
0
要么必须断开,贡献为
要么随意,贡献为
0.5
0.5
只需要利用所有约束条件确定每个位置的贡献就好啦。
我是看得LCF学长的blog
#include<iostream>
#include<cstdio>
using namespace std;
#define RG register
#define MAX 222222
inline int read()
{
RG int x=0,t=1;RG char ch=getchar();
while((ch<'0'||ch>'9')&&ch!='-')ch=getchar();
if(ch=='-')t=-1,ch=getchar();
while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
return x*t;
}
double ans;
int n,a[MAX],d[MAX],s[MAX];
int main()
{
n=read();ans=1;s[1]=1;s[2]=-1;
for(int i=1;i<=n;++i)a[d[i]=read()]=i;
for(int i=1;i<=n;++i)d[a[read()]]=i;
for(int i=1;i<=n;++i)a[d[i]]=i;
for(int i=2;i<=n;++i)if(a[i]<a[i-1])s[i-1]++,s[i]--,++ans;
for(int i=2;i<=n;++i)if(d[i-1]<d[i]-1)s[d[i-1]]++,s[d[i]]--;
for(int i=1,t=0;i<n;++i)t+=s[i],ans+=(!t)?0.5:0;
printf("%.3lf\n",ans+1);
return 0;
}