一、题目
二、解法
首先考虑一下
50
50
50分的做法吧(每个点都可以建存档点),就是设
d
p
[
i
]
dp[i]
dp[i]为
i
i
i走到
n
n
n的期望步数,转移如下(
d
e
g
deg
deg为度数):
d
p
[
i
]
=
1
d
e
g
(
d
p
[
i
+
1
]
+
∑
v
E
[
v
]
+
1
+
d
p
[
i
]
)
dp[i]=\frac{1}{deg}(dp[i+1]+\sum_{v}E[v]+1+dp[i])
dp[i]=deg1(dp[i+1]+v∑E[v]+1+dp[i])其中
E
[
i
]
E[i]
E[i]是
i
i
i点的子树期望深度(也就是回到存档点的期望步数),我们移项,就可以得到:
d
p
[
i
]
=
d
p
[
i
+
1
]
+
∑
v
E
[
u
]
+
1
dp[i]=dp[i+1]+\sum_{v}E[u]+1
dp[i]=dp[i+1]+v∑E[u]+1再考虑
70
70
70分的做法,很容易想到
f
[
i
]
[
j
]
f[i][j]
f[i][j]为
i
i
i到
n
n
n,已经建了
j
j
j个存档点的期望步数,转移如下:
f
[
i
]
[
j
]
=
∑
f
[
k
]
[
j
−
1
]
+
a
[
i
]
[
k
]
f[i][j]=\sum f[k][j-1]+a[i][k]
f[i][j]=∑f[k][j−1]+a[i][k]其中
a
[
i
]
[
j
]
a[i][j]
a[i][j]是指
i
i
i到
j
j
j,存档点是
i
i
i的期望步数,可以用
50
50
50分做法类似的推出:
a
[
i
]
[
j
]
=
a
[
i
]
[
j
−
1
]
×
d
e
g
[
j
−
1
]
+
d
e
g
[
j
−
1
]
+
E
[
j
−
1
]
a[i][j]=a[i][j-1]\times deg[j-1]+deg[j-1]+E[j-1]
a[i][j]=a[i][j−1]×deg[j−1]+deg[j−1]+E[j−1]这里的
E
[
j
−
1
]
E[j-1]
E[j−1]是指的所有和
j
j
j相连的不正确点的
E
E
E值和,这样做的时间复杂度是
O
(
n
2
p
)
O(n^2p)
O(n2p)的。
考虑满分做法,我们观察数据范围,发现 50 ≤ p ≤ n 50\leq p\leq n 50≤p≤n,这说明了 n / p n/p n/p的最大值是 14 14 14,我们考虑一种特殊情况,就是把存档点均匀的放在 [ 1 , n ] [1,n] [1,n]中,可以估算出此时的答案上界大致是 1 e 12 1e12 1e12,而 a a a的增长是十分恐怖的,所以我们可以把步数设为 40 40 40(这其实是 log a n s \log ans logans),就是说我们只考虑 40 40 40以内的转移点,那么时间复杂度降为 O ( n p log a n s ) O(np\log ans) O(nplogans)
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int M = 1505;
int read()
{
int x=0,flag=1;
char c;
while((c=getchar())<'0' || c>'9') if(c=='-') flag=-1;
while(c>='0' && c<='9') x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x*flag;
}
int T,n,m,p,tot,f[M],deg[M];
double E[M],dp[M][M],a[M][M];
struct edge
{
int v,next;
edge(int V=0,int N=0) : v(V), next(N) {}
} e[2*M];
void dfs(int u,int fa)
{
E[u]=0;
if(!deg[u]) E[u]=1;
for(int i=f[u]; i; i=e[i].next)
{
int v=e[i].v;
if(v==fa) continue;
dfs(v,u);
if(fa) E[u]+=1.0/deg[u]*(E[v]+1);
if(!fa) E[u]+=E[v];
}
}
signed main()
{
T=read();
while(T--)
{
tot=0;
memset(f,0,sizeof f);
memset(deg,0,sizeof deg);
n=read();
m=read();
p=read();
for(int i=1; i<=m-n; i++)
{
int u=read(),v=read();
e[++tot]=edge(v,f[u]),f[u]=tot;
e[++tot]=edge(u,f[v]),f[v]=tot;
deg[u]++;
}
for(int i=1; i<=n; i++)
dfs(i,0);
for(int i=1; i<=n; i++)
for(int j=0; j<=p; j++)
dp[i][j]=1e13;
for(int i=1; i<n; i++)
deg[i]++;
for(int i=1; i<n; i++)
{
a[i][i]=0;
for(int j=i+1; j<=min(n,i+40); j++)
a[i][j]=a[i][j-1]*deg[j-1]+deg[j-1]+E[j-1];
}
for(int i=1; i<=p; i++)
dp[n][i]=0;
for(int i=n-1; i>=1; i--)
for(int j=1; j<=p; j++)
for(int k=i+1; k<=n; k++)
{
if(k-i>40) break;
dp[i][j]=min(dp[i][j],dp[k][j-1]+a[i][k]);
}
printf("%.4lf\n",dp[1][p]);
}
}