【LOJ6078】「2017 山东一轮集训 Day7」重排(二分)(概率DP)

传送门


题解:

首先考虑没有自环的情况。

先考虑没有边权重排的情况,这个时候显然是贪心选择最小后继。

然后考虑边权存在重排怎么做。设度数为 d d d,显然要考虑的是 d 2 d^2 d2个后继组合(边 × \times ×点)被选择的情况。

直接排个序,用堆维护匹配的同时算一下条件概率转移即可,精度爆炸的时候要自行退出。

如果有自环,直接二分或者迭代即可。

复杂度就没有哪个地方是满的。


代码:

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

using std::cerr;
using std::cout;

cs double eps=1e-8;

cs int N=1e3+7;

int n,m,S,T;
int el[N],nxt[N],to[N],ec;double w[N];
inline void adde(int u,int v,double vl){
	nxt[++ec]=el[u],el[u]=ec,to[ec]=v,w[ec]=vl;
} 

struct node{
	int v;double w;
	friend bool operator<(cs node &a,cs node &b){
		return a.w>b.w;
	}
};

int ct,ps[N];
double E[N],a[N],b[N];
bool toT[N],vis[N];

inline double calc(){
	std::priority_queue<node> q;
	for(int re i=1;i<=ct;++i)q.push((node){i,a[i]+b[1]}),ps[i]=ct;
	double p=1,res=0;
	while(!q.empty()&&p>0){
		node t=q.top();q.pop();
		int v=t.v;double w=t.w;
		double tmp=p*(ps[v]-v)/(ps[v]-v+1.);
		--ps[v];res+=w*(p-tmp);p=tmp;
		if(ps[v]>=1)q.push((node){v,a[v]+b[ct-ps[v]+1]});
	}
	return res;
}

void dfs(int u){
	vis[u]=true;bool sel=false;
	if(u==T){toT[u]=true;return ;}
	for(int re e=el[u];e;e=nxt[e]){
		if(!vis[to[e]])dfs(to[e]);
		toT[u]|=toT[to[e]];
	}if(!toT[u]){E[u]=1e10;return ;}
	ct=0;for(int re e=el[u];e;e=nxt[e])a[++ct]=w[e],sel|=to[e]==u;
	std::sort(a+1,a+ct+1);
	if(!sel){
		ct=0;for(int re e=el[u];e;e=nxt[e])b[++ct]=E[to[e]];
		std::sort(b+1,b+ct+1);E[u]=calc();return ;
	}
	double l=0,r=1e6;
	while(l+eps<r){
		E[u]=(l+r)*0.5;
		ct=0;for(int re e=el[u];e;e=nxt[e])b[++ct]=E[to[e]];
		std::sort(b+1,b+ct+1);calc()>E[u]?l=E[u]:r=E[u];
	}E[u]=l;
}
signed main(){
#ifdef zxyoi
	freopen("shuffle.in","r",stdin);
#endif
	scanf("%d%d%d%d",&n,&m,&S,&T);
	for(int re i=1;i<=m;++i){
		int u,v;double vl;
		scanf("%d%d%lf",&u,&v,&vl);
		adde(u,v,vl);
	}dfs(S);
	if(!toT[S])puts("-1");
	else printf("%.7lf",E[S]);
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值