题解 P1232 【[NOI2013]树的计数】
在博客食用效果更佳(洛谷图片太大了qwq)
神仙题...
和树的深度有关,由于
为了方便,先把
考虑每个位置是否可以分段,无非
对于
大概图长这样:
可以发现只有这种情况才会出现
所以如果
那么如果
可能有两种情况,
对于
如果
对限制并没有影响
如果
所以仍然无法确定贡献,但是同样可以发现不管是哪种情况对后面的序列都没有影响(里后面节点可以接到
但是第三种情况有点意思:
说明
那么就是说,编号从
而且可以发现,如果有分割那么在之前就被计算过了
(一定存在
所以对于
这个限制可以用一个差分数组维护
最后剩下的都不能确定了,贡献就是
最后一个问题,这些限制是必要的,但是,充分吗?
感性理解一下,很充分,如果怀疑的话打个暴力拍它几个小时,发现没问题,所以是充分的...
并不会证明限制充分
代码很短,但是并不好想...
设
那么对于前面最后一个限制,
注意一开始那些一定要分割的位置已经算过就不会再产生
还有第一个节点一定单独要分一层
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
inline int read()
{
int x=0,f=1; char ch=getchar();
while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); }
return x*f;
}
const int N=2e6+7;
int n;
double ans;
int dfn[N],pos[N],sum[N];//sum是差分数组
//dfn是节点的dfs序,pos是dfn的反数组,所以有 pos[dfn[i]]=i,dfn[pos[i]]=i
inline void mark(int x,int y) { sum[x]++,sum[y+1]--; }//打差分标记
int main()
{
// freopen("1.in","r",stdin);
n=read();
ans=1; mark(1,1);//第一个节点一定要分一层
for(int i=1;i<=n;i++) dfn[read()]=i;//读入初始dfs序
for(int i=1;i<=n;i++) pos[dfn[read()]]=i;//用初始dfs序更新变化后的pos
//上面一行不太好想。因为bfs序重标号了,原来的节点read()就变成了i,所以pos[dfn[read()]]=i
for(int i=1;i<=n;i++) dfn[pos[i]]=i;//用更新后的pos更新dfn
for(int i=1;i<n;i++)//注意i不取n
{
if(dfn[i]>dfn[i+1]) ans++,mark(i,i);//注意这里也要打标记,之后不会再产生0.5的贡献
if(pos[i]<pos[i+1]-1) mark(pos[i],pos[i+1]-1);//打标记
}
int now=0;
for(int i=1;i<n;i++) now+=sum[i],ans+=(now ? 0 : 0.5);//如果这个位置不确定则贡献为0.5
//注意上一行i不取n,因为切的位置只有n-1个
ans+=1;//深度等于分的段数+1
// printf("%.3lf\n%.3lf\n%.3lf",ans-0.001,ans,ans+0.001);//BZOJ上要这样输出...
printf("%.3lf\n",ans);
return 0;
}