• BZOJ3244 NOI2013树的计数(概率期望)


      容易发现的一点是如果确定了每一层有哪些点,树的形态就确定了。问题变为划分bfs序。

      考虑怎样划分是合法的。同一层的点在bfs序中出现顺序与dfs序中相同。对于dfs序中相邻两点依次设为x和y,y至多在x的下一层。特殊的,根单独作为一层。

      这些条件显然是必要的,考虑一种构造方案:对于某个点,如果其在dfs序中前一个点的下一层则直接连边,否则从其bfs序中前一个点(显然是与其在同一层的)的父亲连边。这看起来是没有问题的,所以这些条件也是充分的。

      接下来考虑怎么算答案。如果bfs序中相邻两点被划分在了不同层树高就会++,于是考虑相邻点是否能划在同一层。

      首先如果bfs序中相邻两点在bfs序和dfs序中的相对顺序不同,其必须划分,这保证了第一个条件满足。

      然后对dfs序中相邻两点x和y,其在bfs序中x到y(有序,若x在y后面则该限制无效)这段区间至多被划分一次,这显然保证了第二个条件满足。

      这样可以固定一些位置是否划分,保证了方案合法,划分一次提供1贡献。而对于剩下的位置不管是否划分都是合法的,所以提供0.5的贡献。

      至于具体怎么搞,第一个条件扫一遍即可确定。第二个条件若区间内已被划分打个标记即可。否则虽然看起来有点麻烦,但实际上如果出现这种情况该区间长度只会为1。因为这说明他们之间的dfs序和bfs序相对顺序都是相同的,这仅当两点在同一层才会出现,而这样他们在bfs序中也是相邻的。

      往往很多必要条件组合在一起就是充分的了,大力猜结论不要怂。

      bzoj上莫名其妙要输出三个数。

    #include<iostream> 
    #include<cstdio>
    #include<cmath>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    int read()
    {
        int x=0,f=1;char c=getchar();
        while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
        while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
        return x*f;
    }
    #define N 200010
    int n,a[N],b[N],id_dfs[N],id_bfs[N],f[N],s[N],cnt[N],ans=2;
    int main()
    {
    #ifndef ONLINE_JUDGE
        freopen("bzoj3244.in","r",stdin);
        freopen("bzoj3244.out","w",stdout);
        const char LL[]="%I64d
    ";
    #else
        const char LL[]="%lld
    ";
    #endif
        n=read();
        for (int i=1;i<=n;i++) id_dfs[a[i]=read()]=i;
        for (int i=1;i<=n;i++) id_bfs[b[i]=read()]=i;
        memset(f,255,sizeof(f));f[1]=1;
        for (int i=2;i<n;i++)
        if (id_dfs[b[i]]>id_dfs[b[i+1]]) f[i]=1;
        for (int i=1;i<n;i++) s[i]=s[i-1]+(f[i]>0);
        for (int i=2;i<n;i++)
        if (id_bfs[a[i]]<id_bfs[a[i+1]]&&s[id_bfs[a[i+1]]-1]>s[id_bfs[a[i]]-1])
        cnt[id_bfs[a[i]]]++,cnt[id_bfs[a[i+1]]]--;
        int x=0;
        for (int i=1;i<n;i++)
        {
            x+=cnt[i];
            if (x) f[i]=max(f[i],0);
        }
        for (int i=1;i<n;i++)
        if (f[i]>=0) ans+=f[i]<<1;
        else ans++;
        //printf("%.3f",ans/2.0);
        printf("%.3f
    %.3f
    %.3f",ans/2.0-0.001,ans/2.0,ans/2.0+0.001);
        return 0;
    }
  • 相关阅读:
    Enigmatic Partition【二阶差分】-2020牛客暑期多校8
    Tetrahedron【几何】-2020杭电多校5
    Set1【组合数】-2020杭电多校5
    Paperfolding【组合数】-2020杭电多校5
    并发编程学习总结(二、AQS实现类总结)
    并发编程学习笔记(三十五、线程池源码五,submit方法分析)
    并发编程学习笔记(三十四、线程池源码四,execute方法分析)
    并发编程学习笔记(三十三、线程池源码三,线程池状态)
    并发编程学习笔记(三十二、线程池源码二,ThreadPoolExecutor构造函数)
    并发编程学习笔记(三十一、线程池源码一,工作线程Worker)
  • 原文地址:https://www.cnblogs.com/Gloid/p/9695685.html
Copyright © 2020-2023  润新知