记忆的轮廓

题目描述

四次死亡轮回后,昴终于到达了贤者之塔,当代贤者夏乌拉一见到昴就上前抱住了昴“师傅!你终于回来了!你有着和师傅一样的魔女的余香,肯定是师傅”。
众所周知,大贤者是嫉妒魔女沙提拉的老公,400年前与神龙、剑圣一起封印魔女因子暴走的莎缇拉。在魔女茶会的时候,莎缇拉也表示过对昴浓浓的爱意,昴便是被莎缇拉召唤来异世界的。
而贤者之塔中的资料与试炼,似乎都指向同一种可能性……记忆的轮廓,逐渐显形……

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

数据范围

50<=p<=n<=700,m<=1500,T<=5
数据保证每个除了n的正确节点均有至少2个儿子,至多3个儿子。

dp

设d[i]表示i的直接儿子的个数,设g[i]表示错误节点i期望走多少步会读档。
g[i]=1+1/d[i](g[k]) 其中k是i的儿子。
设s[i]表示正确节点i的错误儿子的g值和。
设a[i][j](i,j) 表示i走到j,中间不存档的期望步数。
a[i][j]=a[i][j-1]+1/d[j-1]+(d[j-1]-1)/d[j-1]*(a[i][j]+1)+1/d[j-1]*s[j-1]
发现两边都有a[i][j],所以我们可以移下项。
a[i][j]=a[i][j-1]*d[j-1]+s[j-1]+1
然后dp,设f[i][j](i) 表示i是当前读档点,还剩下j次存档机会,走到n的期望步数。
f[i][j]=f[k][j-1]+a[i][k]

到这里为止,复杂度为O(n^2p)

观察a数组,我们可以发现它增长特别快,计算一下ans的上界,发现是不超1e12
所以枚举k时,当a[i][k]>=1e12时即可退出

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fod(i,a,b) for(i=a;i>=b;i--)
#define ll long long
using namespace std;
const int ma=1500+5;
int i,j,t,l,n,m,p,num,k[ma],g[ma*2],next[ma*2];
double gg[ma],s[ma],d[ma],f[ma],a[ma][ma],w[ma];
void add(int x,int y){
    next[++num]=k[x];
    k[x]=num,g[num]=y;
}
void dfs(int x){
    int i=k[x];
    d[x]=0;
    while (i>0){
        dfs(g[i]);d[x]++;
        i=next[i];
    }
    if (x>n){
        gg[x]=1;double ss=d[x];ss=1/ss;
        i=k[x];while (i>0) gg[x]+=gg[g[i]]*ss,i=next[i];
    }else{
        i=k[x];s[x]=0;
        while(i>0) s[x]+=gg[g[i]],i=next[i];
    }
}
int main(){
    freopen("memory.in","r",stdin);
    freopen("memory.out","w",stdout);
    scanf("%d",&t);
    while (t>0){
        t--;
        scanf("%d%d%d",&n,&m,&p);num=0;
        memset(k,0,sizeof(k));memset(a,0,sizeof(a));
        fo(i,1,m-n) {
            int x,y;scanf("%d%d",&x,&y);
            add(x,y);
        }
        fo(i,1,n) dfs(i);
        fo(i,1,n-1) d[i]++;
        fo(i,1,n-1)
        fo(j,i+1,n) a[i][j]=a[i][j-1]*d[j-1]+d[j-1]+s[j-1];
        w[n]=f[n]=0;
        fo(i,1,n-1) w[i]=1e12;
        fo(l,1,p-1) {
            fod(i,n-1,1) {
                f[i]=1e12;
                fo(j,i+1,min(n,i+40)) f[i]=min(f[i],w[j]+a[i][j]);
            }
            fo(i,1,n-1) w[i]=f[i];
        }
        printf("%.4lf\n",w[1]);
    }
}
发布了80 篇原创文章 · 获赞 21 · 访问量 4万+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章