BZOJ4899 记忆的轮廓(概率期望+动态规划+决策单调性)

  容易发现跟树没什么关系,可以预处理出每个点若走向分叉点期望走多少步才能回到上个存档点,就变为链上问题了。考虑dp,显然有f[i][j]表示在i~n中设置了j个存档点,其中i设置存档点的最优期望步数。转移枚举下一个存档点设在哪,则有f[i][j]=min(f[k][j-1]+d[i][k]),其中d[i][k]为从i号点存档点走到k号存档点其间没有别的存档点的期望步数。对d数组可以把一堆方程列出来手动加减消元得到式子,n2就可以求出。这样复杂度O(Tn3)。于是直接暴力就在darkbzoj上水过了。或者加一些乱七八糟的剪枝就能跑得飞快。然后有感性理解比较显然的一点是这个东西有决策单调性,于是就能做到O(Tn2logn)。证明估计得列一堆式子不太敢证了。然而由于d数组已经大到爆了精度,需要一些乱七八糟的处理,本来还以为是决策单调性写挂了调了半天一点卵用都没有。尽管这样还是没在bzoj上过掉,不知道bzoj有什么奇怪的精度问题。被恶心死了。

#include<iostream> 
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
#define N 710
#define inf 1000000000
char getc(){char c=getchar();while ((c<'A'||c>'Z')&&(c<'a'||c>'z')&&(c<'0'||c>'9')) c=getchar();return c;}
int gcd(int n,int m){return m==0?n:gcd(m,n%m);}
int read()
{
    int x=0,f=1;char c=getchar();
    while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
    while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
    return x*f;
}
int T,n,m,k,p[N<<2],L[N],R[N],id[N],top,t;
double v[N<<2],d[N][N],f[N][N],son[N<<2];
struct data{int to,nxt;
}edge[N<<2];
void addedge(int x,int y){t++;edge[t].to=y,edge[t].nxt=p[x],p[x]=t;}
void dfs(int k)
{
    for (int i=p[k];i;i=edge[i].nxt)
    {
        son[k]++;
        dfs(edge[i].to);
        v[k]+=v[edge[i].to]+1;
    }
    if (son[k]) v[k]/=son[k];
    son[k]++;
}
double calc(int i,int x,int y){return f[i-1][y]+d[x][y];}
int main()
{
#ifndef ONLINE_JUDGE
    freopen("bzoj4899.in","r",stdin);
    freopen("bzoj4899.out","w",stdout);
    const char LL[]="%I64d\n";
#else
    const char LL[]="%lld\n";
#endif
    T=read();
    while (T--)
    {
        n=read(),m=read(),k=read();
        memset(p,0,sizeof(p));t=0;
        memset(son,0,sizeof(son));
        memset(v,0,sizeof(v));
        for (int i=1;i<=m-n;i++)
        {
            int x=read(),y=read();
            addedge(x,y);
        }
        for (int i=1;i<=n;i++) dfs(i),v[i]++;
        for (int i=1;i<n;i++)
        {
            double t=1;
            for (int j=i;j<n;j++)
            {
                d[i][j+1]=d[i][j]+t/son[j]+t*(son[j]-1)/son[j]*v[j];
                t/=son[j];
            }
            t=1;double s=1;
            for (int j=i;j<n;j++)
            {
                s-=t*(son[j]-1)/son[j];
                d[i][j+1]/=s;
                t/=son[j];
            }
        }
        for (int i=1;i<n;i++) f[1][i]=inf;
        for (int i=1;i<=n;i++)
            for (int j=i+1;j<=n;j++)
            if (d[i][j]>inf) d[i][j]=1ll*inf*(j+1);
        for (int i=2;i<=k;i++)
        {
            top=1;id[1]=n;L[1]=1,R[1]=n-1;
            for (int j=n-1;j>=1;j--)
            {
                int l=1,r=top,x=0;
                while (l<=r)
                {
                    int mid=l+r>>1;
                    if (L[mid]<=j&&R[mid]>=j) {x=mid;break;}
                    else if (L[mid]>j) l=mid+1;
                    else r=mid-1;
                }
                f[i][j]=calc(i,j,id[x]);
                while (top&&R[top]<j&&calc(i,R[top],j)<calc(i,R[top],id[top])) top--;
                l=L[top],r=min(j,R[top])-1,x=L[top]-1;
                /*for (int p=r;p>=l;p--)
                if (calc(i,p,j)<calc(i,p,id[top])) {x=p;break;}*/
                while (l<=r)
                {
                    int mid=l+r>>1;
                    if (calc(i,mid,j)<calc(i,mid,id[top])) x=mid,l=mid+1;
                    else r=mid-1;
                }
                L[top]=x+1;
                if (x) top++,id[top]=j,L[top]=1,R[top]=x;
            }
        }
        /*for (int i=2;i<=k;i++)
            for (int j=n-1;j>=1;j--)
            {
                f[i][j]=inf;
                for (int x=j+1;x<=min(j+7+n/k,n);x++)
                f[i][j]=min(f[i][j],calc(i,j,x));
            }*/
        printf("%.4f\n",f[k][1]);
    }
    return 0;
}

 

posted @ 2018-11-29 23:05  Gloid  阅读(339)  评论(0编辑  收藏  举报