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)