昨天的那个期望题的正解:
相当于每个点作为起点,枚举其终点所获得的期望之和。
可以这样理解,枚举起点的时候,可以发现:
...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的空间来维持这段操作,建树方法相同,但是用数组的话就可以变更树的方向,并不难学习的操作,但是还没有发现好的模板代码。