题意:
一颗树,定义一条路径的权值等于路径的边权之和,需要求这颗树所有路径中权值的最大值

思路:

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

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

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

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

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

于是考虑思路2:

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

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

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

代码:

 #include <bits/stdc++.h>
using namespace std;
long long f[];
// 节点编号[1,n]
// 最大节点数
#define MAXN 200010
// 满足2^M > MAXN
#define M 20
struct node
{
long long to,val;
};
struct tree
{
long long r,x,y;
void clear()
{
r=;
x=y=-;
}
}t[];
vector<node>g[];
long long d[];
long long n;
int a[];
long long ans;
long long ch[MAXN]={}; // 节点i下面的孩子的个数(包括i)
long long fa[MAXN][M+]; // ch[i][j]表示与节点i距离为2^j的i的祖先
long long len[MAXN][M+];
long long deep[MAXN]={}; // i的深度(根节点的深度为1)
// 初始化deep数组,ch数组,fa数组的初始值
// 默认根节点为1
long long dfs(int cur=, int father = ,long long val=)
{
deep[cur] = deep[father]+;
fa[cur][] = father;
len[cur][]=val;
ch[cur] = ;
int sz = g[cur].size();
for(int i=;i<sz;i++)
{
if(g[cur][i].to != father)
ch[cur] += dfs(g[cur][i].to,cur,g[cur][i].val);
}
return ch[cur];
}
// 初始化fa数组
void initFa(int n)
{
for(int i=;i<=;i++)
for(int node=;node<=n;node++)
{
fa[node][i] = fa[fa[node][i-]][i-];
len[node][i]= len[fa[node][i-]][i-]+len[node][i-];
}
}
// 将node上升height的高度
int binaryRaise(int node, int height,long long &val)
{
val=;
for(int i=M; i>=; i--)
{
if(fa[node][i] && height >= (<<i))
{
val+=len[node][i];
node = fa[node][i];
height -= (<<i);
}
}
return node;
}
// a的深度比b大
long long lca(int a, int b)
{
if(a==||b==)
return -;
// 先移动a到与b同样深度的地方
if(deep[a]<deep[b])
swap(a,b);
long long res=;
a = binaryRaise(a, deep[a]-deep[b],res);
if(a==b) // 此时,b就是a和b的公共祖先
//return a;
return res;
for(int i=M;i>=;i--)
{
if(a!=b && fa[a][i]!=fa[b][i])
{
res+=len[a][i];
a = fa[a][i];
res+=len[b][i];
b = fa[b][i];
}
} return res+len[a][]+len[b][];
}
int find(int a)
{
if(f[a]==a||f[a]==-)
return f[a]=a;
f[a]=find(f[f[a]]);
t[a]=t[f[a]];
return f[a];
}
int uni(int a,int b)
{
int x=find(a),y=find(b);
return f[y]=x;
}
bool issame(int a,int b)
{
return find(a)==find(b);
} bool cmp(int a,int b)
{
return d[a]>d[b];
}
void merg(int a,int b)
{
int fb=find(b);
long long l11=lca(t[a].x,t[fb].x);
long long l12=lca(t[a].x,t[fb].y);
long long l21=lca(t[a].y,t[fb].x);
long long l22=lca(t[a].y,t[fb].y);
long long maxl=max(max(max(max(max(l11,l12),l21),l22),t[a].r),t[fb].r);
int x1=t[a].x;
int x2=t[fb].x;
int y1=t[a].y;
int y2=t[fb].y;
if(l11==maxl)
{
t[a].x=x1;
t[a].y=x2;
}
if(l12==maxl)
{
t[a].x=x1;
t[a].y=y2;
}
if(l21==maxl)
{
t[a].x=y1;
t[a].y=x2;
}
if(l22==maxl)
{
t[a].x=y1;
t[a].y=y2;
}
if(t[a].r==maxl)
{
t[a].x=x1;
t[a].y=y1;
}
if(t[fb].r==maxl)
{
t[a].x=x2;
t[a].y=y2;
}
t[a].r=maxl;
f[fb]=a;
}
void put(int p)
{
t[p]=tree{,p,p};
f[p]=p;
for(int i=;i<(int)g[p].size();i++)
{
int to=g[p][i].to;
if(f[to]==-)
continue;
merg(p,to);
}
ans=max(ans,d[p]*t[p].r);
}
int main()
{
freopen("in.txt","r",stdin);
int T;
scanf("%d",&T);
while(T--)
{
memset(ch,,sizeof(ch));
memset(fa,,sizeof(fa));
memset(len,,sizeof(len));
memset(deep,,sizeof(deep));
//scanf("%I64d",&n);
scanf("%lld",&n);
for(int i=;i<=n;i++)
g[i].clear();
for(int i=;i<=n;i++)
{
scanf("%lld",d+i);
a[i-]=i;
}
int x,y;
long long v;
for(int i=;i<n;i++)
{
//scanf("%d%d%I64d",&x,&y,&v);
scanf("%d%d%lld",&x,&y,&v);
g[x].push_back(node{y,v});
g[y].push_back(node{x,v});
}
dfs();
initFa(n);
sort(a,a+n,cmp);
memset(f,-,sizeof(f));
memset(t,,sizeof(t));
ans=-;
for(int i=;i<n;i++)
{
put(a[i]);
}
//printf("%I64d\n",ans);
printf("%lld\n",ans);
}
return ;
}

思路2:对点考虑, 对于当前点s(每次选取重心), 可以求出以它为根, 每一个子树上的点上到s的点权最小值, 和边权和, 然后 把每一个每一个子树分类, 这是为了避免计算的结果在同一个子树里。

然后计算不同子树中的结果, 这里计算结果时需要排个序, 按照点权最小值从大到小排序, 每次选取两个属于不同子树的最大边权和。

如此递归计算即可

 #include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e5+;
int siz[MAXN], n, val[MAXN];
bool center[MAXN]; typedef pair<int, int>pii;
struct Edge{
int to, next;
int c;
}e[MAXN * ];
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 = ;
memset(head, -, sizeof (head));
memset(center, , 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, -);
int m = ;
siz[s] = ;
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 = ;
long long sum1 = Mval[idx-].first.second, sum2 = ;
int t1 = Mval[idx-].second;
for (int i = idx-; i >= ; 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, , tot).second;
center[g] = true;
long long res = ;
idx = ;
//tim++;
Mval[idx++] = make_pair(make_pair(val[g],), 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 = ;
scanf ("%d", &n);
for (int i = ; i < n; i++){
scanf ("%d", val+i+);
}
for (int i = ; i < n-; 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(, n);
printf("%lld\n", res); }
return ;
}

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

  1. 2018 计蒜之道复赛 贝壳找房魔法师顾问(并查集+dfs判环)

    贝壳找房在遥远的传奇境外,找到了一个强大的魔法师顾问.他有 22 串数量相同的法力水晶,每个法力水晶可能有不同的颜色.为了方便起见,可以将每串法力水晶视为一个长度不大于 10^5105,字符集不大于  ...

  2. 2019icpc徐州站 Cat 计蒜客 - 42540 && The Answer to the Ultimate Question of Life, The Universe, and Everything. 计蒜客 - 42545

    VJ链接:https://vjudge.net/contest/412095#problem/A Cat 计蒜客 - 42540 题意: 给你一个区间[L,R],给你现在拥有的钱S.你需要从[L,R] ...

  3. 计蒜客 作弊揭发者(string的应用)

    鉴于我市拥堵的交通状况,市政交管部门经过听证决定在道路两侧安置自动停车收费系统.当车辆驶入车位,系统会通过配有的摄像头拍摄车辆画面,通过识别车牌上的数字.字母序列识别车牌,通过连接车管所车辆信息数据库 ...

  4. 计蒜客的一道题dfs

    这是我无聊时在计蒜客发现的一道题. 题意: 蒜头君有一天闲来无事和小萌一起玩游戏,游戏的内容是这样的:他们不知道从哪里找到了N根不同长度的木棍, 看谁能猜出这些木棍一共能拼出多少个不同的不等边三角形. ...

  5. 计蒜客模拟赛5 D2T1 成绩统计

    又到了一年一度的新生入学季了,清华和北大的计算机系同学都参加了同一场开学考试(因为两校兄弟情谊深厚嘛,来一场联考还是很正常的). 不幸的是,正当老师要统计大家的成绩时,世界上的所有计算机全部瘫痪了. ...

  6. 计蒜客 等边三角形 dfs

    题目: https://www.jisuanke.com/course/2291/182238 思路: 1.dfs(int a,int b,int c,int index)//a,b,c三条边的边长, ...

  7. 计蒜客 方程的解数 dfs

    题目: https://www.jisuanke.com/course/2291/182237 思路: 来自:https://blog.csdn.net/qq_29980371/article/det ...

  8. 计蒜客 买书 dfs

    题目: https://www.jisuanke.com/course/2291/182236 思路: 递归解决,从第一本书开始,每本书都有两种选择: //index是book里面每本书价格的下标, ...

  9. 计蒜客:Entertainment Box

    Ada, Bertrand and Charles often argue over which TV shows to watch, and to avoid some of their fight ...

随机推荐

  1. 对openflow 1.0协议的扩展

    通过这几天对openvswitch代码的分析,以及项目的须要,须要对openflow 1.0进行一定的扩展,发现网上没有这方面的教程,尽管在搞懂ovs代码架构,floodlight controlle ...

  2. 实战:mysql版本号升级

    /***************************************************** mysql 5.6.19 升级到5.6.21 ********************** ...

  3. 静态代码检查工具 cppcheck 的使用

      CppCheck是一个C/C++代码缺陷静态检查工具.不同于C/C++编译器及其它分析工具,CppCheck只检查编译器检查不出来的bug,不检查语法错误.所谓静态代码检查就是使用一个工具检查我们 ...

  4. [转] Ubuntu 12.04下LAMP安装配置 (Linux+Apache+Mysql+PHP)

    我是一个Linux新手,想要安装一台Ubuntu 12.04版的Linux服务器,用这台服务器上的LAMP套件来运行我自己的个人网站.LAMP套件就是 “Linux+Apache+Mysql+PHP这 ...

  5. shape 填充 圆角矩形 圆形 环形

    属性 使用中可能出现的问题: 如果在某些手机中使用 shape 出现黑色填充背景,设置<solid android:color="@color/transparent"/&g ...

  6. hdu 2218

    题意: 切蛋糕问题 水题...... AC代码: #include <iostream> using namespace std; int main() { int a[6],i,n; c ...

  7. spring依赖注入源码分析和mongodb自带连接本地mongodb服务逻辑分析

    spring依赖注入本质是一个Map结构,key是beanId,value是bean对应的Object. autowired是怎么将定义的接口与对应的bean类建立联系? <bean name= ...

  8. TextView drawablePadding没有效果

    1.当TextView 设置宽度设置为match_parent的时候 TextView drawablePadding没有效果 ,字设置了center位置,但是和左边的图片离开很远 2.当TextVi ...

  9. Android Translate 动画跳跃和缓慢移动

    1.动画跳跃:在动画结束的时候设置位置 Animation.AnimationListener listener = new Animation.AnimationListener() { @Over ...

  10. SQL Server 和CLR集成

    通过在 Microsoft SQL Server 中托管 CLR(称为 CLR 集成),可以在托管代码中编写存储过程.触发器.用户定义函数.用户定义类型和用户定义聚合函数. 因为托管代码在执行之前会编 ...