2019浙大宁波理工校赛E 雷顿女士与平衡树(并查集)

这种题一般都是算贡献,对于这题,我们显然发现,每个点作为最大值肯定是有一定范围的。

因此我们考虑将点排序,计算每个点作为最大值的答案。

枚举每个点,之后遍历他的邻边,如果有之前被标记过的点,说明他是最大值,因此用并查集维护后,计算集合乘积即可

#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;
}
View Code
没有人不辛苦,只有人不喊疼
原文地址:https://www.cnblogs.com/ctyakwf/p/14002295.html