这种题一般都是算贡献,对于这题,我们显然发现,每个点作为最大值肯定是有一定范围的。
因此我们考虑将点排序,计算每个点作为最大值的答案。
枚举每个点,之后遍历他的邻边,如果有之前被标记过的点,说明他是最大值,因此用并查集维护后,计算集合乘积即可
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<ll,int> pll; const int N=1e6+10; const int mod=1e9+7; int p[N],sz[N]; int h[N],ne[N],e[N],idx; int st[N]; int n; struct node{ int w,id; }s[N]; void add(int a,int b){ e[idx]=b,ne[idx]=h[a],h[a]=idx++; } void init(){ int i; for(i=1;i<=n;i++){ p[i]=i; sz[i]=1; } } bool cmp(node a,node b){ return a.w<b.w; } int find(int x){ if(x!=p[x]){ p[x]=find(p[x]); } return p[x]; } int main(){ ios::sync_with_stdio(false); int t; cin>>t; while(t--){ idx=0; cin>>n; init(); int i; memset(h,-1,sizeof h); memset(st,0,sizeof st); for(i=1;i<=n;i++){ cin>>s[i].w; s[i].id=i; } for(i=1;i<n;i++){ int a,b; cin>>a>>b; add(a,b); add(b,a); } sort(s+1,s+1+n,cmp); ll ans=0; for(i=1;i<=n;i++){ int tmp=s[i].id; st[tmp]=1; for(int j=h[tmp];j!=-1;j=ne[j]){ int k=e[j]; if(st[k]){ int pa=find(k); int pb=find(tmp); ans=(ans+1ll*sz[tmp]*sz[pa]%mod*s[i].w)%mod; if(pa!=pb){ p[pa]=pb; sz[pb]+=sz[pa]; } } } } reverse(s+1,s+1+n); memset(st,0,sizeof st); ll ans1=0; init(); for(i=1;i<=n;i++){ int tmp=s[i].id; st[tmp]=1; for(int j=h[tmp];j!=-1;j=ne[j]){ int k=e[j]; if(st[k]){ int pa=find(k); int pb=find(tmp); ans1=(ans1+1ll*sz[tmp]*sz[pa]%mod*s[i].w)%mod; if(pa!=pb){ p[pa]=pb; sz[pb]+=sz[pa]; } } } } cout<<(ans-ans1+mod)%mod<<endl; } return 0; }