題目描述
四次死亡輪迴後,昴終於到達了賢者之塔,當代賢者夏烏拉一見到昴就上前抱住了昴“師傅!你終於回來了!你有着和師傅一樣的魔女的餘香,肯定是師傅”。
衆所周知,大賢者是嫉妒魔女沙提拉的老公,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期望走多少步會讀檔。
設s[i]表示正確節點i的錯誤兒子的g值和。
設a[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]
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]);
}
}