LOJ6078 「2017 山东一轮集训 Day7」重排

Link
先考虑没有自环的情况。
按拓扑序逆序进行dp,假设我们已经求完了(u)的所有后继的(f),现在考虑如何求(f_u)
(u)(k)个出点和(k)条出边一一匹配得到(k^2)个二元组,然后按权值和从小到大排序,依次考虑最优方案大于等于当前二元组权值的概率。
有了自环之后会出现自己转移到自己的情况,这可以通过迭代/二分求解。

#include<cmath>
#include<queue>
#include<cstdio>
#include<vector>
#include<algorithm>
using db=double;
const int N=1007;const db eps=1e-8;
int n,m,s,t,tot,head[N],vis[N],is[N],pos[N];db f[N];std::vector<db>a,b;
struct edge{int v,next;db w;}e[N];
struct node{int x;db w;node(int X=0,db W=0):x(X),w(W){}};
int operator<(const node&a,const node&b){return a.w>b.w;}
std::priority_queue<node>q;
void add(){int u,v;db w;scanf("%d%d%lf",&u,&v,&w),e[++tot]=edge{v,head[u],w},head[u]=tot;}
int have_same(int u){for(int i=head[u];i;i=e[i].next)if(e[i].v==u)return 1;return 0;}
void geta(int u){a.clear();for(int i=head[u];i;i=e[i].next)a.push_back(e[i].w);std::sort(a.begin(),a.end());}
void getb(int u){b.clear();for(int i=head[u];i;i=e[i].next)b.push_back(f[e[i].v]);std::sort(b.begin(),b.end());}
db calc()
{
    while(!q.empty()) q.pop();
    int num=(int)a.size();db p=1,ans=0;
    for(int i=0;i<num;++i) q.emplace(i,a[i]+b[0]),pos[i]=num;
    while(!q.empty()&&p>eps)
    {
	auto[x,w]=q.top();q.pop(),ans+=w*p/(pos[x]-x),p-=p/(pos[x]-x);
        if(--pos[x]>x) q.emplace(x,a[x]+b[num-pos[x]]);
    }
    return ans;
}
db find(int u)
{
    db l=0,r=100,mid;
    for(int t=0;t<30;++t) f[u]=mid=(l+r)/2,getb(u),calc()>mid? l=mid:r=mid;
    return l;
}
void dfs(int u)
{
    vis[u]=1;
    if(u==t) return is[u]=1,void();
    for(int i=head[u],v;v=e[i].v,i;is[u]|=is[v],i=e[i].next) if(!vis[v]) dfs(v);
    if(!is[u]) return f[u]=1e10,void();
    geta(u),getb(u),have_same(u)? f[u]=find(u):f[u]=calc();
}
int main()
{
    scanf("%d%d%d%d",&n,&m,&s,&t);
    for(int i=1;i<=m;++i) add();
    dfs(s),printf("%.7lf",is[s]? f[s]:-1);
}
原文地址:https://www.cnblogs.com/cjoierShiina-Mashiro/p/12828549.html