記憶的輪廓

題目描述

四次死亡輪迴後,昴終於到達了賢者之塔,當代賢者夏烏拉一見到昴就上前抱住了昴“師傅!你終於回來了!你有着和師傅一樣的魔女的餘香,肯定是師傅”。
衆所周知,大賢者是嫉妒魔女沙提拉的老公,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萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章