达到今日更新量
题目让我们求所有简单路径上最大值减去最小值的总和
实际上就是所有简单路径的最大值总和减去所有简单路径上最小值总和
然后分别求所以简单路径的极值,下面以最大值为例:
我刚开始想到了非常SB的做法:枚举最大值x,设比x大的数为y,实际上有很多y,如果y是x的祖先,那么点对范围锁定在了y的子树以内,如果y不是x的祖先,那么能够与x构成点对的点数量就要减去y的子树的个数,然后又要讨论不经过x的点对,减掉,非常的麻烦阿,而且直接T飞
为了避免其他大的值带来的干扰,因为无影响,所以我可以从小到大排序,就能保证当前这一个是暂时最大的数,发现加点的话不好计算,转化一波,如果每次添加一条边进来,最大值就是将两个连通块合并,贡献很好算,有个比较常用的技巧:两个点u,v,权值为a[u],a[v],那么如果要经过这条边,必然经过两个端点,因为极值重复取是无影响的,直接将边权w=max(a[u],a[v])就行了,代码很好写阿
#include<bits/stdc++.h>
#define vd void
#define int long long
#define MAXN 1000005
int gi(){char c;int x=0,f=0;while(!isdigit(c=getchar()))f|=(c=='-');while(isdigit(c))x=(x*10)+(c^48),c=getchar();return f?-x:x;
}
struct E{int u,v,w;
}e[MAXN];
int n,a[MAXN],fa[MAXN],siz[MAXN];
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
int gmax(){int res=0;for(int i=1;i<n;i++)e[i].w=std::max(a[e[i].u],a[e[i].v]);for(int i=1;i<=n;i++)fa[i]=i,siz[i]=1;std::sort(e+1,e+n,[&](E a,E b){return a.w<b.w;}); for(int i=1;i<n;i++){int fu=find(e[i].u),fv=find(e[i].v);if(fu!=fv)res+=siz[fu]*siz[fv]*e[i].w,fa[fv]=fu,siz[fu]+=siz[fv];}return res;
}
int gmin(){int res=0;for(int i=1;i<n;i++)e[i].w=std::min(a[e[i].u],a[e[i].v]);for(int i=1;i<=n;i++)fa[i]=i,siz[i]=1;std::sort(e+1,e+n,[&](E a,E b){return a.w>b.w;}); for(int i=1;i<n;i++){int fu=find(e[i].u),fv=find(e[i].v);if(fu!=fv)res+=siz[fu]*siz[fv]*e[i].w,fa[fv]=fu,siz[fu]+=siz[fv];}return res;
}signed main(){n=gi();for(int i=1;i<=n;i++)a[i]=gi();for(int i=1;i<n;i++)e[i].u=gi(),e[i].v=gi();printf("%lld\n",gmax()-gmin());return 0;
}
- 为了避免其他值的影响,我们可以以某种方式排序
- 点权转化边权有些不能转化,但这道题可以,因为贡献重复是无影响的