您的位置:首页 > 其它

4850. 【GDOI2017模拟11.3】记忆的轮廓

2016-11-14 21:40 232 查看

题目描述

通往贤者之塔的路上,有许多的危机。

我们可以把这个地形看做是一颗树,根节点编号为1,目标节点编号为n,其中1-n的简单路径上,编号依次递增,在[1,n]中,一共有n个节点。

我们把编号在[1,n]的叫做正确节点,[n+1,m]的叫做错误节点。一个叶子,如果是正确节点则为正确叶子,否则称为错误叶子。

莎缇拉要帮助昴到达贤者之塔,因此现在面临着存档位置设定的问题。为了让昴成长为英雄,因此一共只有p次存档的机会,其中1和n必须存档。被莎缇拉设置为要存档的节点称为存档位置。

当然不能让昴陷入死循环,所以存档只能在正确节点上进行,而且同一个节点不能存多次档。因为通往贤者之塔的路上有影响的瘴气,因此莎缇拉假设昴每次位于树上一个节点时,都会等概率选择一个儿子走下去。每当走到一个错误叶子时,再走一步就会读档。

具体的,每次昴到达一个新的存档位置,存档点便会更新为这个位置(假如现在的存档点是i,现在走到了一个存档位置j>i,那么存档点便会更新为j)。读档的意思就是回到当前存档点。

初始昴位于1,当昴走到正确叶子n时,便结束了路程。莎缇拉想知道,最优情况下,昴结束路程的期望步数是多少?

50%,n=p

70%,50<=p<=n<=500

100%,50<=p<=n<=700,m<=1500,T<=5

数据保证每个除了n的正确节点均有至少2个儿子,至多3个儿子。

分析

对付概率、期望的题目,经常会出现需要解方程的时候。

定义边界:由于题目限制,到此状态的时候有准确的期望值。

这种时候有两种情况:1,有边界,这种就可以通过dp或记忆化搜索逐步解单一方程,求出一些期望;2,无边界,可能需要高斯消元解方程,求出所有解。

回看这道题,看似无法分析,一看就蒙。很多期望题都是这样,这个时候我们要找切入点。

先考虑n=p时的情况,这个时候,从1到n的简单路径都设为存档点,那么我们只用知道,对于这条简单路径上的一个节点i,它往下走的期望是多少。我们知道在这个时候,一旦走到错误节点,那肯定回到自己。设i往下走的期望步数f[i],走到错误叶子的期望步数g[i],有tot[i]个儿子,那么



这样求出f[i]后就很轻易地弄出n=p时的答案了嘛。

那么一般情况呢?

设fx[i][j]为第i个点走到最终点(我们把简单路径编号),设置了j个存档点的期望步数,那么对于一个fx[i][j]就可以转移到fx[1~i-1][j+1]嘛。为了好做,我们设a[i][j]表示i设存档点,走到j的期望步数。然后再列出一个方程,解之即求出a。

考虑此时时间复杂度:3次方,不行。

此时观察a的方程后,发现增量的单调性,可以用单调队列进行优化,降一级复杂度。

另一个较好的思路是:我们发现a[i]增长十分恐怖,可以设定阈值,爆了就不算下去了,因为那样太蠢了,想象一下前面一大段没有存档点,最后存档点扎堆····

我们可以均匀地放存档点,把此时的期望步数设为阈值,这样便可以放心DP了,事实证明,这样很快。

代码

#include<cstdio>
#include<algorithm>
using namespace std;
#define fo(i,j,k) for(i=j;i<=k;i++)
#define fd(i,j,k) for(i=j;i>=k;i--)
typedef long long ll;
const int N=2005;//记得改回来!!!!!!!!!!!!!
int b
,next
,first
,first1
,next1
,t1,tt;
int T,i,j,n,m,x,y,p;
double sum
,b1
,c1
,res,xs,f
;
void cr(int x,int y)
{
tt++;
b[tt]=y;
next[tt]=first[x];
first[x]=tt;
sum[x]+=1;
}
void cr1(int x,int y,double z)
{
t1++;
b1[t1]=y;//db
next1[t1]=first1[x];
first1[x]=t1;
c1[t1]=z;
}
void dfs(int x,int y,double z)
{
for(int p=first[x];p;p=next[p])
dfs(b[p],y+1,z/sum[x]);
if (!first[x])
cr1(i,y,z);
}
int main()
{
freopen("memory.in","r",stdin);
freopen("memory.out","w",stdout);
scanf("%d",&T);
while (T--)
{
fo(i,1,m) first[i]=first1[i]=0;
tt=t1=0;
scanf("%d%d%d",&n,&m,&p);
fo(i,1,m-n)
{
scanf("%d%d",&x,&y);
cr(x,y);
}
fo(i,1,n)
{
sum[i]+=1;
dfs(i,0,1);
}
f
=0;
fd(i,n-1,1)
{
res=(f[i+1]+1)/sum[i];
xs=0;
for(p=first1[i];p;p=next1[p])
{
xs+=c1[p];
res+=c1[p]*(b1[p]+1);
}
f[i]=res/(1-xs);
}
printf("%.4lf\n",f[1]);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: