话说BZOJ 是不是死了啊
(已经没有传送门了)
设 $f[i][j]$ 表示走到第 $j$ 个位置确定了 $i$ 个存档点时的最小代价,并强制第 $j$ 个位置有一个存档点
那么设 $cst[i][j]$ 表示存档点在 $i$ ,走到位置 $j$ 的代价, $f$ 有转移:
$f[i][j]=f[i-1][k]+cst[k][j]$
考虑 $cst[i][j]$ 如何计算,对每个起点 $i$ 求出 $i$ 指向的错误节点 $v$ 重新走回 $i$ 的期望步数 $dis[v]$,这个可以简单树形 $dp$ 得到
那么设 $val=sum_{v in son(j-1)} (cst[i][j-1]+1+dis[v])$ ,那么有 $cst[i][j]=cst[i][j-1]+1+val$
这里 $val$ 意义显然为从 $j-1$ 走到 $j$ 的期望代价,因为从 $j-1$ 到各个后继的概率一样,所以期望就是把每个后继各走一遍
加上 $cst[i][j-1]+1$ 显然是因为存档点在 $i$ ,要走到 $j-1$ 的后面一个儿子代价就是 $cst[i][j-1]+1$
然后这样就可以 $n^3$ 跑 $dp$ 了,考虑如何优化
把 $cst$ 的表打出来发现 $cst[i][j]$ 比 $cst[i][j-1]$ 的值大很多,发现 $cst$ 增长很快,显然超过一次函数
那么对于 $a<b<c<d$ ,就有 $cst[a][c]+cst[b][d]<=cst[a][d]+cst[b][c]$,即满足四边形不等式
所以对于 $f[i][j]$ 的最优转移点 $k$ ,$f[i][j+1]$ 的最优转移点一定不小于 $k$(不然我们直接从 $f[i-1][k]$ 转移会更优,写写式子就知道了)
然后用决策单调性分治优化即可
#include<iostream> #include<cstdio> #include<algorithm> #include<cstring> #include<cmath> #include<vector> using namespace std; typedef long long ll; typedef double db; inline int read() { int x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9') { if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9') { x=(x<<1)+(x<<3)+(ch^48); ch=getchar(); } return x*f; } const int N=2007; const db INF=1e99; int T,n,m,p; vector <int> V[N]; db f[N][N],cst[N][N],dis[N]; void dfs(int x,int fa) { int sz=V[x].size(); if(sz==1) { dis[x]=1; return; } dis[x]=0; for(int i=0;i<sz;i++) { int &v=V[x][i]; if(v==fa) continue; dfs(v,x); dis[x]+=dis[v]+1; } dis[x]/=sz-1; } void solve(int i,int l,int r,int ql,int qr) { int mid=l+r>>1,pos=ql,R=min(qr,mid-1); f[i][mid]=INF; for(int k=ql;k<=R;k++) { db val=f[i-1][k]+cst[k][mid]; if(val<f[i][mid]) f[i][mid]=val,pos=k; } if(l<mid) solve(i,l,mid-1,ql,pos); if(mid<r) solve(i,mid+1,r,pos,qr); } int main() { T=read(); while(T--) { n=read(),m=read(),p=read(); for(int i=1;i<=m-n;i++) { int a=read(),b=read(); V[a].push_back(b); V[b].push_back(a); } for(int i=1;i<=n;i++) for(auto A: V[i]) dfs(A,i); for(int i=1;i<=n;i++) { cst[i][i]=0; for(int j=i+1;j<=n;j++) { cst[i][j]=cst[i][j-1]+1; for(auto A: V[j-1]) cst[i][j]+=cst[i][j-1]+1 + dis[A]; } } f[1][1]=0; for(int i=2;i<=n;i++) f[1][i]=INF; for(int i=2;i<=p;i++) solve(i,1,n,1,n); printf("%.4lf ",f[p][n]); for(int i=1;i<=m;i++) V[i].clear(); } return 0; }