Rencontre (2020 China Collegiate Programming Contest, Weihai Site)

题目来源

https://codeforces.ml/gym/102798/problem/C

题意分析

  题目意思大概是,给出一颗树,有三个集合,其中包括树上的一些点(不同集合之间会出现相同情况),每次从三个集合中各取一个点,在树上找到一个点到这三个点的距离最小。问所有组合的期望。

思路分析

  dp选手耻辱补题!

  之前在考场上想到过一点,就是在树上某三个点形成的一种组合,他们的最终情况一定是三个点之间的所有的边只走一次。因为总能找出一个点,使得之间的边只能走一次吧。。当时想到这里,之后的就都是错误做法了。。
  结束之后,感觉这道题其实可以从边的角度去思考。对于一种组合来说,笼统的来说的话,三个点之间包含的那些点才能被算进贡献。而对于一种组合来说,其最小距离f = (dis(a, b) + dis(b, c) + dis(a, c)) / 2。然后,对于一条边来说,其贡献为两边分别a,b,c类型点的乘积。然后最后将所有边的贡献加在一起,最后除以2就可以了。具体处理方法是对整颗树进行dfs,第一次统计该节点(包括该节点)下面各种类型点的个数,同时标记该节点与父节点之间的边。第二次dfs的时候,算出所有边的贡献即可。具体细节详见代码。

code

 1 #include <bits/stdc++.h>
 2 
 3 #define eps 1e-6
 4 #define int long long
 5 using namespace std;
 6 const int maxn = 2e5 + 7;
 7 
 8 int head[maxn], nxt[maxn << 1], ver[maxn << 1], wi[maxn << 1];
 9 int tot;
10 bool vis[3][maxn];
11 int l[maxn], siz[3][maxn], p[maxn];
12 int ans[3];
13 
14 void adde(int u, int v, int w){
15     ++tot; ver[tot] = v; wi[tot] = w; nxt[tot] = head[u]; head[u] = tot;
16     ++tot; ver[tot] = u; wi[tot] = w; nxt[tot] = head[v]; head[v] = tot;
17 }
18 
19 void dfs(int u, int f){
20 //    cout << "u: " << u << " f: " << f << endl;
21     for (int i=0; i<3; i++){
22         if (vis[i][u]) siz[i][u] ++;
23     }
24     for (int i=head[u]; i; i=nxt[i]){
25         int v = ver[i];
26         if (v == f) {p[u] = i; continue;}
27         dfs(v, u);
28         for (int i=0; i<3; i++) siz[i][u] += siz[i][v];
29     }
30 }
31 
32 void solve(int u, int f){
33     ans[0] += (siz[0][u] * (l[1] - siz[1][u]) + siz[1][u] * (l[0] - siz[0][u])) * wi[p[u]];
34     ans[1] += (siz[1][u] * (l[2] - siz[2][u]) + siz[2][u] * (l[1] - siz[1][u])) * wi[p[u]];
35     ans[2] += (siz[0][u] * (l[2] - siz[2][u]) + siz[2][u] * (l[0] - siz[0][u])) * wi[p[u]];
36     for (int i=head[u]; i; i=nxt[i]){
37         int v = ver[i];
38         if (v == f) continue;
39         solve(v, u);
40     }
41 }
42 
43 signed main(){
44     int n; scanf("%lld", &n);
45     for (int i=1; i<n; i++){
46         int u, v, w;
47         scanf("%lld%lld%lld", &u, &v, &w);
48         adde(u, v, w);
49     }
50 //    cout << 1 << endl;
51     for (int i=0; i<3; i++){
52         int k; scanf("%lld", &k);
53         l[i] = k;
54         for (int j=1; j<=k; j++) {
55             int tmp; scanf("%lld", &tmp);
56             vis[i][tmp] = true;
57         }
58     }
59 //    cout << 2 << endl;
60 
61     dfs(1, 0);
62 //    for (int i=1; i<=n; i++) cout << siz[0][i] << " ";
63 //    cout <<endl;
64 //    for (int i=1; i<=n; i++) cout << siz[1][i] << " ";
65 //    cout <<endl;
66 //    for (int i=1; i<=n; i++) cout << siz[2][i] << " ";
67 //    cout <<endl;
68     solve(1, 0);
69 //
70 //    cout << "!!!!!!" << endl;
71 //    for (int i=0; i<3; i++) cout << ans[i] << " ";
72 //    cout << endl;
73 
74     double ans0 = 1.0 * ans[0] / (l[0] * l[1]);
75     double ans1 = 1.0 * ans[1] / (l[1] * l[2]);
76     double ans2 = 1.0 * ans[2] / (l[0] * l[2]);
77 //    cout << ans0 << " " << ans1 << " " << ans2 << endl;
78     printf("%.9f\n", (ans0 + ans1 + ans2) / 2);
79     return 0;
80 }
81 
82 //Rencontre (2020 China Collegiate Programming Contest, Weihai Site)

 

posted @ 2020-11-19 22:03  Rain_island  阅读(126)  评论(0编辑  收藏  举报
Title