题目大意:给出一棵树,其中每个点有其点权。求树上每对点间路径上的极差之和。
题解:鉴于n<=1e6,O( n ^ 2)的算法会T飞。因此可以考虑将极差拆为sigma max - sigma min。
因此得出这样一个做法:
第一步,将点按点权排序。
第二步,从小到大向图中加点,用并查集维护块的大小,此时新块中的新点对路径上最大值就是新点的权值。
第三步,同第二步求最小值。
最后相减。
代码:
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define N 1000050 #define ll long long inline int rd() { int f=1,c=0;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){c=10*c+ch-'0';ch=getchar();} return f*c; } int n,v[N],hed[N],cnt; struct EG { int to,nxt; }e[2*N]; void ae(int f,int t) { e[++cnt].to = t; e[cnt].nxt = hed[f]; hed[f] = cnt; } ll ans; struct node { int x; }p[N]; bool cmp1(node a,node b) { return v[a.x] < v[b.x]; } int fa[N],siz[N]; bool vis[N]; int findfa(int x) { if(x==fa[x])return x; return fa[x]=findfa(fa[x]); } int main() { n=rd(); for(int i=1;i<=n;i++)v[i]=rd(); for(int f,t,i=1;i<n;i++) { f=rd(),t=rd(); ae(f,t),ae(t,f); } for(int i=1;i<=n;i++) { p[i].x=i; fa[i]=i; siz[i]=1; } sort(p+1,p+1+n,cmp1); for(int i=1;i<=n;i++) { int u = p[i].x; vis[u]=1; ll tmp1=0,tmp2=0; for(int j=hed[u];j;j=e[j].nxt) { int to = e[j].to; if(!vis[to])continue; int ff = findfa(to); siz[u]+=siz[ff]; tmp1+=1ll*siz[ff],tmp2+=1ll*siz[ff]*siz[ff]; fa[ff]=u; } ans+=1ll*v[u]*tmp1; tmp1 = tmp1*tmp1; ans+=1ll*v[u]*(tmp1-tmp2)/2; } for(int i=1;i<=n;i++) { fa[i]=i; vis[i]=0; siz[i]=1; } for(int i=n;i>=1;i--) { int u = p[i].x; vis[u]=1; ll tmp1 = 0,tmp2 = 0; for(int j=hed[u];j;j=e[j].nxt) { int to = e[j].to; if(!vis[to])continue; int ff = findfa(to); siz[u]+=siz[ff]; tmp1 += 1ll*siz[ff],tmp2+=1ll*siz[ff]*siz[ff]; fa[ff] = u; } ans-=1ll*v[u]*tmp1; tmp1 = tmp1*tmp1; ans-=1ll*v[u]*(tmp1-tmp2)/2; } printf("%lld ",ans); return 0; }