#bzoj3376#快递配对(树 + 重心)

xiaoxiao2021-02-28  84

3376: 【geng4512膜你题1】快递配对

时间限制: 1 Sec  内存限制: 233 MB

【问题描述】

azui大爷厌倦了每天在家颓废的生活,于是开始打工送快递。Jeremy同学不想让azui大爷太轻松,于是想让他送快递的路程尽可能的长。

一句话来说就是:

给出一棵n个点的树,将这n个点两两配对,求所有可行的方案中配对两点间的距离的总和最大为多少。

 

【输入格式】

一个数n(1<=n<=100,000,n保证为偶数)

接下来n-1行每行三个数x,y,z表示有一条长度为z的边连接x和y(0<=z<=1,000,000,000)

【输出格式】

      一个数表示答案。

【样例输入】

6

1 2 1

1 3 1

1 4 1

3 5 1

4 6 1

 

【样例输出】

7

 

【样例解释】

(1,2)(3,4)(5,6)分别配对

 

【数据范围】

对于20%的数据,n <= 14

对于另20%的数据,所给的树是一条链。

对于100%的数据,n <= 100000

第二题看着会感觉很熟悉的样子,很可能会想到最长链等一系列的东西,

但是后来想了很多,也画了很多,有一个错误的思路,然后是一个正确的思路。

先说说错误的。

我想,对于每一个非叶子节点来说,

它下面的子树最好要想办法穿过它,到它的上面去,和上面的点配对。

然后这个点和它的父亲节点之间的边,将会被通过sum[i]次(sum[i]表示以i为根的子树的总结点数(包括i))

然后我把1当做根,跑一遍Dfs。

这个思路是错的,为什么呢,

因为并不能保证所有的点都一定能通过它的父亲节点,

所以有边会被多算,

而且以谁为根也不能确定(不能粗暴地定为1, 退化成链就是例子),

枚举的话会超时。

再说后来想到的正解,

以前上课的时候,听过一个结论,也就是一个证明一样的东西,

对于每条边,它左,右的总结点数,较小的那个会是这条边的贡献(即最多被通过的次数)

就是把较少的那边的点全部跟较多的那边的点配对,

至于为什么一定能配对成功,那就用重心去证明,

重心:以它为根的树最大子树的size最小(比起以其他节点为根)

但是这题我考试的时候还是有问题,虽然后来想出了正解,但是真的一定要注意long long的问题,乘法最好记得开long long,教训阿(直接被某学长的数据砍掉90分)

Code:

#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> using namespace std; typedef long long LL; const int Max = 100005; struct node{ int v, len, nxt; }edge[Max << 1]; struct node1{ int l, r, len; }L[Max]; LL Ans; int N, cnt; int sum[Max], fir[Max]; void getint(int & num){ char c; int flg = 1; num = 0; while((c = getchar()) < '0' || c > '9') if(c == '-') flg = -1; while(c >= '0' && c <= '9'){ num = num * 10 + c - 48; c = getchar();} num *= flg; } void addedge(int a, int b, int c){ edge[++ cnt].v = b, edge[cnt].len = c, edge[cnt].nxt = fir[a], fir[a] = cnt; } void Count_P(int r, int fa){ sum[r] = 1; for(int i = fir[r]; i; i = edge[i].nxt) if(edge[i].v != fa){ Count_P(edge[i].v, r); sum[r] += sum[edge[i].v]; } } int main(){ /*int size = 16 << 20; char *p = (char*)malloc(size) + size; __asm__("movl %0, %%esp\n" :: "r"(p));*/ //freopen("pairing.in", "r", stdin); //freopen("pairing.out", "w", stdout); getint(N); int x, y, z; for(int i = 1; i < N; ++ i){ getint(x), getint(y), getint(z); addedge(x, y, z); addedge(y, x, z); L[i].l = x, L[i].r = y, L[i].len = z; } Count_P(1, -1); for(int i = 1; i < N; ++ i){ int t = min(sum[L[i].l], sum[L[i].r]); Ans += (LL)L[i].len * min(N - t, t); } printf("%lld\n", Ans); return 0; }

转载请注明原文地址: https://www.6miu.com/read-73658.html

最新回复(0)