[BZOJ4899]记忆的轮廓

记忆的轮廓

题目描述

通往贤者之塔的路上,有许多的危机。
我们可以把这个地形看做是一颗树,根节点编号为1,目标节点编号为n,其中1-n的简单路径上,编号依次递增,在[1,n]中,一共有n个节点。我们把编号在[1,n]的叫做正确节点,[n+1,m]的叫做错误节点。一个叶子,如果是正确节点则为正确叶子,否则称为错误叶子。莎缇拉要帮助昴到达贤者之塔,因此现在面临着存档位置设定的问题。
为了让昴成长为英雄,因此一共只有p次存档的机会,其中1和n必须存档。被莎缇拉设置为要存档的节点称为存档位置。当然不能让昴陷入死循环,所以存档只能在正确节点上进行,而且同一个节点不能存多次档。因为通往贤者之塔的路上有影响的瘴气,因此莎缇拉假设昴每次位于树上一个节点时,都会等概率选择一个儿子走下去。每当走到一个错误叶子时,再走一步就会读档。具体的,每次昴到达一个新的存档位置,存档点便会更新为这个位置(假如现在的存档点是i,现在走到了一个存档位置j>i,那么存档点便会更新为j)。读档的意思就是回到当前存档点。初始昴位于1,当昴走到正确节点n时,便结束了路程。莎缇拉想知道,最优情况下,昴结束路程的期望步数是多少?

输入格式

第一行一个正整数T表示数据组数。
接下来每组数据,首先读入三个正整数n,m,p。
接下来m-n行,描述树上所有的非正确边(正确边即连接两个正确节点的边)
用两个正整数j,k表示j与k之间有一条连边,j和k可以均为错误节点,也可以一个为正确节点另一个为错误节点。
数据保证j是k的父亲。
50<=p<=n<=700,m<=1500,T<=5。
数据保证每个正确节点均有至少2个儿子,至多3个儿子。

输出格式

T行每行一个实数表示每组数据的答案。请保留四位小数。

样例

样例输入

1
3 7 2
1 4
2 5
3 6
3 7

样例输出

9.0000

数学是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 }
View Code

 

posted @ 2019-08-03 19:34  hzoi_X&R  阅读(274)  评论(0编辑  收藏  举报