BZOJ 4899 记忆的轮廓

话说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;
}
原文地址:https://www.cnblogs.com/LLTYYC/p/11692720.html