Luogu P3978 [TJOI2015] 概率论

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Luogu P3978 [TJOI2015] 概率论相关的知识,希望对你有一定的参考价值。

定义 \\(f_i\\)\\(i\\) 个节点组成的二叉树数量,\\(g_i\\)\\(i\\) 个节点组成的二叉树的叶子节点个数之和

设当前 \\(i\\) 个节点组成的二叉树有 \\(a\\) 个叶子,容易发现分别删掉其中的 \\(1\\) 个叶子节点就能得到一个对应的 \\(i - 1\\) 个节点的二叉树,总共会有 \\(a\\) 颗,可以发现每一个叶子节点对应的二叉树都为 \\(g_i\\) 产生了 \\(1\\) 的贡献

再设有 \\(b\\) 个节点有 \\(1\\) 个孩子,\\(c\\) 个节点有 \\(2\\) 个孩子,首先有个很显然的结论 \\(a + b + c = i\\) 不进行证明,其次能发现还有个结论 \\(b + 2\\times c = i - 1\\),证明考虑除了根节点其他节点都有父亲,\\(b\\) 代表着有 \\(b\\) 个儿子节点的 \\(b\\) 个父亲,\\(c\\) 代表 \\(2\\times c\\) 个节点的 \\(c\\) 个父亲,所以和为除掉根节点的 \\(i - 1\\) 个节点

能发现对于这颗二叉树,要多一个叶子节点可以在 \\(a\\) 个节点的任意一个儿子下挂,\\(b\\) 个节点的剩余的那个儿子下挂,便有 \\(2\\times a + b\\) 个位置可以挂,根据上述的 \\(2\\) 个结论可以推出对应数量 \\(2\\times a + b = 2\\times (a + b + c) - (b + 2\\times c) = 2\\times i - (i - 1) = i + 1\\)
所以可以知道每颗 \\(i - 1\\) 节点数的二叉树都可对 \\(g_i\\) 产生 \\(i\\) 的贡献,所以可得到 \\(g_i = f_i - 1\\times n\\)

接下来考虑得到 \\(f_i\\),考虑枚举左子树的节点个数 \\(j\\),去掉根节点,右子树节点个数即为 \\(i - j - 1\\),注意也可以有一个子树没有节点,所以可得到 \\(f_i = \\sum\\limits_j = 0^i - 1 f_j\\times f_i - j - 1\\)
然后发现这是个卡特兰数,所以 \\(f_i = \\fracC_2n^nn + 1\\)

最后答案根据定义显然即为 \\(\\fracg_nf_n\\),当然是需要拆一下式子的
\\(\\fracg_nf_n = \\fracf_n - 1\\times nf_n = \\frac\\frac(2n - 2)!(n - 1)!\\times (n - 1)!\\times n\\times n\\frac(2n)!n!\\times n!\\times (n + 1) = \\frac(2n - 2)!\\times n!\\times n!\\times n\\times (n + 1)(2n)!\\times (n - 1)!\\times (n - 1)!\\times n = \\frac1\\times n\\times n\\times 1\\times (n + 1)(2n)\\times (2n - 1)\\times 1\\times 1\\times 1 = \\fracn\\times (n + 1)2\\times (2n - 1)\\)

// lhzawa(https://www.cnblogs.com/lhzawa/)
#include<bits/stdc++.h>
using namespace std;
int main() 
    long long n;
    scanf("%lld", &n);
    long double ans = 1.0 * n * (n + 1) / 2 / (2 * n - 1);
    printf("%.9Lf\\n", ans); 
    return 0;

luogu题解 P3763 [TJOI2017]DNA

  • 题目链接:

    https://www.luogu.org/problemnew/show/P3763

  • 思路:

    首先我们要用到Rabin-Karp哈希,其实就是这个:

    \\(w_{str}\\)=(\\(a_0\\) \\(p^{n-1}\\)+\\(a_1\\) \\(p^{n-2}\\)+...+\\(a_{n-1}\\) \\(p^0\\))

    所以

    \\(w_{pre_{i-1}}\\) \\(=(\\) \\(a_0\\) \\(p^{i-1}\\)+\\(a_1\\) \\(p^{i-2}\\)+...+\\(a_{i-1}\\) \\(p^0\\))

    \\(w_{pre_{j}}\\) \\(=(\\) \\(a_0\\) \\(p^{j}\\)+\\(a_1\\) \\(p^{j-1}\\)+...+\\(a_{j}\\) \\(p^0\\))

    所以

    \\(w_{str_{i,j}}\\)

    \\(=(\\) \\(a_i\\) \\(p^{j-i}\\)+\\(a_{i+1}\\) \\(p^{j-i-1}\\)+...+\\(a_{j}\\) \\(p^0\\))

    \\(=\\) \\(w_{pre_{j}}\\) \\(-\\) \\(w_{pre_{i-1}}\\) \\(p^{j-i+1}\\)

    注意了,我这里并没有取模,而是直接直接用unsigned long long 自然溢出,这样更快
    

    然而,一般人都是用二分确定右端点,我自己摸索出了一个骚操作(好像又在哪里听过)----用倍增

    因为在这道题中我觉得二分的上下界可能相差比较大,虽然理论上二分平均情况下更好,但在这道题中实际测出来用倍增更快(如果之后有人用二分跑得还比我快就无视我这句话)。

    思路就是这样,其他一些细节就见代码吧.

  • 代码:

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <iostream>
#define ri int 
const int maxn=100005;
typedef long long   ll;
typedef unsigned long long ull;
char a[maxn],b[maxn];
ull q[maxn],aht[maxn],bht[maxn];
int cnt=0,lena,lenb;
inline void Hash(){
    ll x=0;
    for(ri i=0;a[i];i++){
        x=x*131+a[i]-31;
        aht[i]=x;
    //	cout<<x<<i<<endl;
    }x=0;
    for(ri i=0;b[i];i++){
        x=x*131+b[i]-31;
        bht[i]=x;
    }x=0;
    return ;
}
inline int solve(int x,int y){
    int k=0,p=1;
    x++,y++;
  	while(p!=0){  
        if((aht[x+k+p-1]-aht[x-1-1]*q[k+p+1])==(bht[y+k+p-1]-bht[y-1-1]*q[k+p+1]))k+=p,p*=2;
        else p=p/2;//cout<<l<<\'*\'<<r<<\'*\'<<mid<<endl;
        while(x+k+p>lena||y+k+p>lenb)p=p/2;
    }
    if(a[x-1]==b[y-1])k++;
    return k;
}
inline bool ok(int i){
      int la=0,k;
      for(ri j=1;j<=3;j++){
           k=solve(i,la);
           i+=k+1,la+=k+1;
           if(la>=lenb){return 1;}//cout<<i<<\' \'<<la<<endl;
      }
         k=solve(i,la); 
         i+=k;la+=k;
      //   cout<<i<<\' \'<<la<<endl;
         if(la>=lenb) return 1;
         return 0;
}
int main()
{
    int t;
    scanf("%d",&t);
    q[0]=1;
    for(ri i=1;i<=100001;i++)
        q[i]=(ull)q[i-1]*131;
  //预处理幂,这个技巧来自https://www.cnblogs.com/sineagle/p/8490655.html
    while(t--){
       int cnt=0;
       scanf("%s",a);
       scanf("%s",b);
       lena=strlen(a),lenb=strlen(b);
       if(lena<lenb){printf("0\\n");continue;}
       Hash();
       for(ri i=0;i<lena-lenb+1;i++){
         if(ok(i))cnt++;
       }
       printf("%d\\n",cnt);
       memset(aht,0,sizeof(aht));
       memset(bht,0,sizeof(bht));
    }
    return 0;
}
  • 后记:

    luogu上最快的一次跑了240ms,然而还是比不过BZOJ的dalao

以上是关于Luogu P3978 [TJOI2015] 概率论的主要内容,如果未能解决你的问题,请参考以下文章

LG P3978 [TJOI2015]概率论

并不对劲的bzoj4001:loj2105:p3978:[TJOI2015]概率论

[TJOI2015]概率论

[Luogu 3973] TJOI2015 线性代数

bzoj4001: [TJOI2015]概率论

[TJOI2015] 概率论 - Catalan数