[BZOJ4899]记忆的轮廓
记忆的轮廓
题目描述
输入格式
输出格式
样例
数学是OI生涯难以跨过的一道坎,概率期望是数学这道坎上的一个大坑,看见概率期望就懵
这道题我真的膜拜出题人,因为他的数据里其实有50%是n==p(然而我们的题面和BZOJ上都没有写,其实写了我也不一定能拿50),先搞定这50%吧
50%
$n==p$也就是每个点都可以存档,那可以先求出每个错误叶子期望走多少步可以读档,由当前错误叶子的错误儿子推来,儿子的期望乘以概率$\frac{1}{du[i]}$为可由此儿子转移而来的期望步数,由儿子还需再走一步才可到当前错误叶子
$g[i]=\sum\limits_{j为i的儿子}\frac{g[j]+1}{du[i]}$
我们设$f[i]$表示由正确叶子i到n所要的期望步数,倒着推,那$f[n]=0$
$f[i]=\frac{f[i+1]+1}{du[i]}+\sum\limits_{j是i的儿子}\frac{g[j]+f[i]+1}{du[i]}$
此处$j$为$i$的错误儿子,我们把$∑g[i]$提前预处理出来,存到$sum[i]$就避免了一层循环,通分移项化简得
$f[i]=du[i]+f[i+1]+sum[i]$
最后的$f[1]$就是要的结果
70%
50分搞定了就要想办法解决$n≠p$的情况了,那我们就用$f[i][j]$记录到点$i$还剩$j$次存档机会到$n$的期望步数,先处理出$a[i][j]$$i$为存档点到正确叶子$j$的期望步数,你肯定是不断的向后走,所以$j>i$,预处理是$a[i][i]=0$,进行递推
$a[i][j]=a[i][j-1]+\sum\limits_{k是j的错误儿子}\frac{g[k]+a[i][j]+1}{du[j]}$
还是移项
$a[i][j]=du[j]*a[i][j-1]+du[j]+sum[j]$
$f[i][j]=\min(f[i][j],f[k][j-1]+a[i][k])$
$k$为可转移的点,$f[1][p]$就是结果
不过这个70%的程序到我手里50TLE,明明有跟我写法一样70TLE的,我也不知道我为啥就50,然后就去看了出题人的优化,他说a最大是$2^{40}$,最多转移40步以内的就够用了,咱也没看太懂,咱也没地问,咱就是个OI蒟蒻,然后我就限制了这个转移步数,然后我就用70%的题解A掉了这道题,100%的那个算法我大概看了一眼,要单队优化,要二分,蒟蒻伤不起啊
以下是摘自出题人博客关于最多40步之内转移的解释,不过我最开始看一个同学的题解,15步就足够A掉这道题的,可能大概是步数多了之后的期望步数全被舍掉了,没有被利用的价值吧,毕竟要求的是最优解
1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #define maxm 1600 5 #define pp 40 6 using namespace std; 7 int t,js,n,m,p; 8 int head[maxm],to[maxm],xia[maxm],son[maxm]; 9 double cwqw[maxm],sum[maxm]; 10 double a[maxm][maxm],f[maxm][maxm]; 11 bool pd[maxm]; 12 int read() 13 { 14 int e=0,f=1; char ch=getchar(); 15 while(ch<'0'||ch>'9') 16 { 17 if(ch=='-') f=-1; 18 ch=getchar(); 19 } 20 while(ch>='0'&&ch<='9') {e=(e<<3)+(e<<1)+(ch^48); ch=getchar();} 21 return e*f; 22 } 23 void add(int x,int y) 24 { 25 to[++js]=y; 26 xia[js]=head[x]; 27 head[x]=js; 28 } 29 void dfs(int x) 30 { 31 pd[x]=1; cwqw[x]=1.0000; 32 for(int i=head[x];i;i=xia[i]) 33 { 34 int ls=to[i]; dfs(ls); 35 cwqw[x]+=cwqw[ls]/(double)son[x]; 36 } 37 } 38 int main() 39 { 40 t=read(); 41 while(t--) 42 { 43 n=read(); m=read(); p=read(); js=0; 44 for(int i=1;i<=m;++i) head[i]=to[i]=xia[i]=son[i]=sum[i]=pd[i]=0; 45 for(int i=1;i<=m-n;++i) {int j=read(),k=read(); add(j,k); son[j]++;} 46 for(int i=n+1;i<=m;++i) 47 if(pd[i]==0) dfs(i); 48 for(int i=1;i<=n;++i) 49 for(int j=head[i];j;j=xia[j]) sum[i]+=cwqw[to[j]]; 50 for(int i=1;i<=n;++i) 51 { 52 a[i][i]=0; 53 for(int j=i+1;j<=n;++j) 54 a[i][j]=(double)((double)son[j-1]+1)*a[i][j-1]+(double)son[j-1]+sum[j-1]+1; 55 } 56 memset(f,127,sizeof(f)); f[n][1]=0; 57 for(int j=2;j<=p;++j) 58 for(int i=1;i<=n;++i) 59 for(int k=i+1;k<=n&&k-i<=pp;++k) f[i][j]=min(f[i][j],f[k][j-1]+a[i][k]); 60 printf("%0.4lf\n",f[1][p]); 61 } 62 return 0; 63 }