您的位置:首页 > 其它

计蒜客 444 / xtuoj 1024 京东的物流路径(并查集+离线lca)或者 (点分治)

2015-07-28 11:25 316 查看
题意:
一颗树,定义一条路径的权值等于路径的边权之和,需要求这颗树所有路径中权值的最大值

思路:

考虑到路径权值与点权的最值有关,而最值的问题通常可以通过排序就行处理,于是想到先把点权排序。

容易看出如果某条路径的权值是通过某个点算出的最小 ,那么肯定这条路径肯定不会经过权值更小的点,于是有了两种处理思路

1.按点权从小到大删点,对于即将删除的点,比他权值小的点已经被删去了,所以只要在当前状态的森林里找一条最长路径乘以次点权就可以更新答案

2.按点权从大到小加点,显然新加进来的点权值最小,当前树里的任何路径的点权最小值都不会小于新加进来的点,所以可以通过维护直径来更新答案

对于思路1,如果是一条链的话,对于一个将要删除的点,可以直接二分得到当前点附近已经被删除的点并更新答案,然而在树里面就不太好从处理了,至少我肯定是不会的

于是考虑思路2:

加点建树很明显可以通过并查集维护

但是在树的合并过程中怎么维护直径呢?这里需要用到一个定理。。一颗树的直径的两个端点一定是他子树直径的端点

于是就可以进行直径的维护了,具体求距离可以选择logn的lca

代码:

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e5+10;
int siz[MAXN], n, val[MAXN];
bool center[MAXN];

typedef pair<int, int>pii;
struct Edge{
int to, next;
int c;
}e[MAXN * 2];
int head[MAXN], edge_tot;
void Add_Edge(int x, int y, int z){
e[edge_tot].to = y;
e[edge_tot].next = head[x];
e[edge_tot].c = z;
head[x] = edge_tot++;
}

void init (){
edge_tot = 0;
memset(head, -1, sizeof (head));
memset(center, 0, sizeof (center));
}

pair <pair<long long, long long>, int>Mval[MAXN];
pii Find(int s, int pa, int tot) {
pii res = make_pair(INT_MAX, -1);
int m = 0;
siz[s] = 1;
for (int i = head[s]; ~i; i = e[i].next) {
int  u = e[i].to;
if (u == pa || center[u]) {
continue;
}
res = min(res, Find(u, s, tot));
siz[s] += siz[u];
m = max(m, siz[u]);
}
m = max(m, tot-siz[s]);
return min(res, make_pair(m, s));
}

int idx, tim;
void Get_Min_Sum(int u, int pa, long long minval, long long sum){
Mval[idx++] = make_pair(make_pair(minval, sum), tim);
for (int i = head[u]; ~i; i = e[i].next){
int v = e[i].to;
if (v != pa && !center[v]){
Get_Min_Sum(v, u, min(minval, (long long)val[v]), sum+e[i].c);
}
}
}
long long sub_solve(){
sort (Mval, Mval+idx);
long long res = 0;
long long sum1 = Mval[idx-1].first.second, sum2 = 0;
int t1 = Mval[idx-1].second;
for (int i = idx-2; i >= 0; i--){
if (Mval[i].second != t1){
res = max(res, Mval[i].first.first*(Mval[i].first.second+sum1));
}else{
res = max(res, Mval[i].first.first*(Mval[i].first.second+sum2));
}
long long tmp = Mval[i].first.second;
if (tmp > sum1){
if (Mval[i].second == t1){
sum1 = tmp;
}else{
sum2 = sum1;
//t2 = t1;
sum1 = tmp;
t1 = Mval[i].second;
}

}else{
if (tmp > sum2 && Mval[i].second != t1){
sum2 = tmp;
//t2 = Mval[i].second;
}
}

}
return res;
}
long long solve (int u, int tot){
int g = Find(u, 0, tot).second;
center[g] = true;
long long res = 0;
idx = 0;
//tim++;
Mval[idx++] = make_pair(make_pair(val[g],0), tim);
for (int i = head[g]; ~i; i = e[i].next){
int v = e[i].to;
int cost = e[i].c;
if (!center[v]){
tim++;
Get_Min_Sum(v, g, min(val[v], val[g]), cost);
}
}
res = max(res, sub_solve());
for (int i = head[g]; ~i; i = e[i].next){
int v = e[i].to;
if (!center[v]){
res = max(res, solve(v, siz[v]));
}
}
return res;
}

int main()
{
//freopen("in.txt", "r", stdin);
int T;
scanf ("%d", &T);
while (T--){
init();
tim = 0;
scanf ("%d", &n);
for (int i = 0; i < n; i++){
scanf ("%d", val+i+1);
}
for (int i = 0; i < n-1; i++){
int u, v, c;
scanf ("%d%d%d", &u, &v, &c);
Add_Edge(u, v, c);
Add_Edge(v, u, c);
}
long long res = solve(1, n);
printf("%lld\n", res);

}
return 0;
}


View Code
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: