2020 China Collegiate Programming Contest, Weihai Site C. Rencontre(树形dp+期望)

题目:C. Rencontre

大致题意:有n个旅馆,有3个集合分别包含了各自的旅馆。
在每个集合中选择一个旅馆之后确定了3个旅馆的位置,再选择一个旅馆v,使得三个旅馆到v旅馆的总距离最小,问路线的期望总长度。

画图可以发现,树上的三个点找到一个点使之距离最小,必是三个点中的一个,将其中一个视作中点,但是确定三个点之后,还得计算是哪一个最小的,由于有一个是中点,可以直接相加三个点的互相距离最后除以2。
证明
在这里插入图片描述
比 较 x + y + z 和 2 m i n 的 大 小 比较x+y+z和2min的大小 x+y+z2min,两端乘以一个2,利用不等式 x + y > m i n x+y>min x+y>min即可证明。

E ( x ) = ∑ ∑ ∑ 1 2 ∗ ( d i s ( 1 , 2 ) + d i s ( 2 , 3 ) + d i s ( 1 , 3 ) ) ∗ p E(x)=\sum\sum\sum\frac{1}{2}*(dis(1,2)+dis(2,3)+dis(1,3))*p E(x)=21(dis(1,2)+dis(2,3)+dis(1,3))p
由于n范围大还得继续化简,可以从边入手,分开成两个集合,判断分割的数量,例如({1},{2,3})以此。分情况讨论。

#include<bits/stdc++.h>
#define mmp make_pair
#define inf 0x3f3f3f3f
#define llinf 0x7fffffffffffffff
using namespace std;
typedef long long ll;
typedef pair<int,int> PP;
typedef double ld;
const ll mod=1e9+7;
const int maxn=3e6+10;
vector<PP>hh[200010];
set<int>gym[5];
int dp[200010][4];
void dfs(int u,int f) {
    dp[u][1]=dp[u][2]=dp[u][3]=0;
	set<int> :: iterator it1,it2,it3;
    it1=gym[1].find(u);
    it2=gym[2].find(u);
    it3=gym[3].find(u);
    if(it1 != gym[1].end()) dp[u][1]=1;
    if(it2 != gym[2].end()) dp[u][2]=1;
    if(it3 != gym[3].end()) dp[u][3]=1;
    for(int i=0;i<hh[u].size();++i) {
        int v=hh[u][i].first;
        if(v==f) continue;
        dfs(v,u);
        dp[u][1]+=dp[v][1];
        dp[u][2]+=dp[v][2];
        dp[u][3]+=dp[v][3];
    }
    return ;
}
double ans=0;
void dfsans(int u,int f) {
    for(int i=0;i<hh[u].size();++i) {
        int v=hh[u][i].first;
        if(v==f) continue;
        ans+=hh[u][i].second*(((dp[1][1]-dp[v][1])*1.0)/(dp[1][1]*1.0)) * ((dp[v][2]*1.0)/(dp[1][2]*1.0)) * ((dp[v][3]*1.0)/(dp[1][3]*1.0));
        ans+=hh[u][i].second*(((dp[1][2]-dp[v][2])*1.0)/(dp[1][2]*1.0)) * ((dp[v][1]*1.0)/(dp[1][1]*1.0)) * ((dp[v][3]*1.0)/(dp[1][3]*1.0));
        ans+=hh[u][i].second*(((dp[1][3]-dp[v][3])*1.0)/(dp[1][3]*1.0)) * ((dp[v][2]*1.0)/(dp[1][2]*1.0)) * ((dp[v][1]*1.0)/(dp[1][1]*1.0));
        int temp1=dp[1][1]-dp[v][1];
        int temp2=dp[1][2]-dp[v][2];
        int temp3=dp[1][3]-dp[v][3];
        ans+=hh[u][i].second*((dp[v][1]*1.0)/(dp[1][1]*1.0)) * ((temp2*1.0)/(dp[1][2]*1.0)) * ((temp3*1.0)/(dp[1][3]*1.0));
        ans+=hh[u][i].second*((dp[v][2]*1.0)/(dp[1][2]*1.0)) * ((temp1*1.0)/(dp[1][1]*1.0)) * ((temp3*1.0)/(dp[1][3]*1.0));
        ans+=hh[u][i].second*((dp[v][3]*1.0)/(dp[1][3]*1.0)) * ((temp2*1.0)/(dp[1][2]*1.0)) * ((temp1*1.0)/(dp[1][1]*1.0));
        dfsans(v,u);
    }
    return ;
}
int main() {
	int n;
	scanf("%d",&n);
	for(int i=1;i<n;++i) {
		int x,y;double d;
		scanf("%d %d %lf",&x,&y,&d);
		hh[x].push_back(mmp(y,d));
		hh[y].push_back(mmp(x,d));
	}
	for(int i=1;i<=3;++i) {
		int m; scanf("%d",&m);
		for(int j=1;j<=m;++j) {
			int x; scanf("%d",&x);
			gym[i].insert(x);
		}
	}
	ans=0.0;
	dfs(1,0);
	dfsans(1,0);
	printf("%.8lf",ans);
	return 0;
}

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值