2018年8月17日暑假训练日记

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/m0_37772713/article/details/81781210

昨天的那个期望题的正解:

相当于每个点作为起点,枚举其终点所获得的期望之和。

可以这样理解,枚举起点的时候,可以发现:

...011...110...一个这样的字符串,只需要dp获得011...110这个串的概率,乘以这一段的取值,就是这一段贡献的期望,因为就相当于其他的点为任意情况而不考虑其贡献得到的期望,由于是对每个点作为起点并枚举其终点,因此不会造成重复,合理利用了期望的性质

#include<iostream>

#include<cstring>

#include<cmath>

#include<cstdio>

#include<algorithm>

#define mo 1000000007

using namespace std;

long long p[1010];

long long ip[1010];

long long a[1010];

long long dp[1010][1010];

long long qpow(long long x,long long y){

    if (x==0)return 0;

    long long ans=1;

    long long k=x;

    while(y){

        if(y&1)ans=ans*k%mo;

        k=k*k%mo;

        y>>=1;

    }

    return (ans+mo)%mo;

}

void get(long long m){

    long long i,j;

    for (i=1;i<=1000;i++){

        a[i]=qpow(i,m);

    }

}

int main(){

    long long n,m;

    long long i,j,k;

    while (scanf("%lld%lld",&n,&m)!=EOF){

        get(m);

        long long temp=qpow(100,mo-2);

        for (i=1;i<=n;i++){

            scanf("%lld",&p[i]);

            ip[i]=(100-p[i])*temp%mo;

            p[i]=p[i]*temp%mo;

        }

        p[0]=p[n+1]=0;

        ip[0]=ip[n+1]=100*temp%mo;

        long long ans=0;

        for (i=1;i<=n;i++){

            dp[i][i-1]=1;

            for (j=i;j<=n;j++){

                dp[i][j]=dp[i][j-1]*p[j]%mo;

                ans=(ans+dp[i][j]*ip[i-1]%mo*ip[j+1]%mo*a[j-i+1]%mo+mo)%mo;

            }

        }

        printf("%lld\n",ans);

    }

}

而用期望dp的方法解释就是:

设前面串的1的连续长度的期望为x,则得到了下一位的期望为:(x+1)^m-x^m

可以用二项展开枚举他每一次方的期望,然后合并得到。

【BZOJ4318】OSU!以这个题目改造的题目,我得到了m次方的通解为:

#include<iostream>

#include<cstring>

#include<cmath>

#include<cstdio>

#include<algorithm>

#define mo 1000000007

using namespace std;

long long a[1010];

long long b[1010];

double p[100010];

double l[100010][10];

double ans[100010];

long long qpow(long long a,long long b){

    long long ans=1;

    long long k=a;

    while(b){

        if(b&1)ans=ans*k%mo;

        k=k*k%mo;

        b>>=1;

    }

    return (ans+mo)%mo;

}

void get(){

    long long i;

    a[0]=1;

    b[0]=1;

    a[1]=1;

    b[1]=1;

for (i=2;i<1010;i++){

        a[i]=((a[i-1]*i)%mo+mo)%mo;

b[i]=(qpow(a[i],mo-2)+mo)%mo;

}

}

long long zuhe(long long n,long long m){

    if (n<m||m<0) return 0;

    return  a[n]*b[m]%mo*b[n-m]%mo;

}

int main(){

    long long n,m;

    long long i,j,k;

    get();

    while (scanf("%lld",&n)!=EOF){

        m=3;

        long long temp=qpow(100,mo-2);

        memset (l,0,sizeof(l));

        memset (ans,0,sizeof(ans));

        for (i=1;i<=n;i++){

            scanf("%lf",&p[i]);

            //p[i]=p[i]*temp%mo;

            for (j=1;j<=m-1;j++){

                l[i][j]=(l[i-1][j]+1);

                for (k=1;k<=j-1;k++){

                    l[i][j]=(l[i][j]+zuhe(j,k)*l[i-1][k]);

                }

                l[i][j]=l[i][j]*p[i];

            }

            double temp=1.0;

            for (j=1;j<=m-1;j++){

                temp=temp+zuhe(m,j)*l[i-1][j];

            }

            ans[i]=ans[i-1]+p[i]*temp;

        }

        printf("%lf\n",ans[n]);

    }

}

/*

3

0.5

0.5

0.5

*/

这里可以根据需要调整m,但是昨天的题目正好卡掉了这个n*(m+1)*m/2的算法,要是m是500就可以很简单的通过,但是对于这个题,可以通过维护一个组合数的方式来解决,但是上面的方法更好理解,可惜超时,这里说的可以用第二类斯特林数进行转换,但是并不理解为何,斯特林数可以表示x的幂次,但是我们要对期望做运算。题解也没有做出更加深刻的解释。

然后看了一下可持久化线段树的写法,以前一直知道有怎么个东西, 也知道是干什么的,但是一直没有学习,今天拓展kmp看的有点迷,就看了一下可持久化线段树。

当然,线段树还是log2进行修改和查询,但是这里要求记录过程,就是对上面某一位置的某个数字进行更改或者查询操作,当然,使用n个线段树肯定是mle+tle。这里,其实并不难理解,以前根树以及左右孩子的标号是固定的,这里用数组替代。若需要修改某一次的操作下的值,则新开log2n的空间来维持这段操作,建树方法相同,但是用数组的话就可以变更树的方向,并不难学习的操作,但是还没有发现好的模板代码。

 

猜你喜欢

转载自blog.csdn.net/m0_37772713/article/details/81781210