CodeForces 915F Imbalance Value of a Tree

题意

给一棵 (n) 个点的树,点有点权,设 (max(x,y)) 表示 (x)(y) 路径中所有点的点权最大值,(min(x,y)) 表示路径上点权的最小值,求:

[sumlimits_{i=1}^{n}sum limits_{j=i+1}^{n}(max(i,j)-min(i,j))]

( exttt{Data Range:}1leq nleq 10^6)

题解

简单题。

将这个式子拆成两个,一个是所有的 (max),另一个是所有的 (min),然后分开求解。首先讨论 (max)

如果是边权的话可以按照边权从小到大加边合并联通块。对于一条边 ((u,v,w)) 来说,它对答案的贡献是 (w imes sz_u imes sz_v),用并查集维护连通块的大小即可 (O(nlog n)) 完成。

但是因为这个题是点权就不行,需要转化成边权。一个常见的 trick(但是我今天第一次接触到)是将连接 (u)(v) 的边的边权变成 (u,v) 两点点权的最大值,因为与一个数多次取 (max) 并不会改变最终结果。

对于 (min) 来说也是一样的,于是这个题就做完了。

代码

#include<bits/stdc++.h>
using namespace std;
typedef int ll;
typedef long long int li;
const ll MAXN=1e6+51;
struct Edge{
    ll from,to,mn,mx;
    inline bool operator <(const Edge &rhs)const
    {
        return mn>rhs.mn;
    }
    inline bool operator >(const Edge &rhs)const
    {
        return mx<rhs.mx;
    }
};
Edge ed[MAXN];
ll n,from,to;
li mx,mn;
ll x[MAXN],ffa[MAXN],sz[MAXN];
inline ll read()
{
    register ll num=0,neg=1;
    register char ch=getchar();
    while(!isdigit(ch)&&ch!='-')
    {
        ch=getchar();
    }
    if(ch=='-')
    {
        neg=-1;
        ch=getchar();
    }
    while(isdigit(ch))
    {
        num=(num<<3)+(num<<1)+(ch-'0');
        ch=getchar();
    }
    return num*neg;
}
inline ll find(ll x)
{
    return x==ffa[x]?x:ffa[x]=find(ffa[x]);
}
inline void merge(ll x,ll y)
{
    ll fx=find(x),fy=find(y);
    fx!=fy?ffa[fy]=fx,sz[fx]+=sz[fy],sz[fy]=0:1;
}
int main()
{
    n=read();
    for(register int i=1;i<=n;i++)
    {
        x[i]=read(),ffa[i]=i,sz[i]=1;
    }
    for(register int i=1;i<n;i++)
    {
        from=read(),to=read();
        ed[i]=(Edge){from,to,min(x[from],x[to]),max(x[from],x[to])};
    }
    sort(ed+1,ed+n);
    for(register int i=1;i<n;i++)
    {
        mn+=(li)sz[find(ed[i].from)]*sz[find(ed[i].to)]*ed[i].mn;
        merge(ed[i].from,ed[i].to);
    }
    sort(ed+1,ed+n,greater<Edge>());
    for(register int i=1;i<=n;i++)
    {
        ffa[i]=i,sz[i]=1;
    }
    for(register int i=1;i<n;i++)
    {
        mx+=(li)sz[find(ed[i].from)]*sz[find(ed[i].to)]*ed[i].mx;
        merge(ed[i].from,ed[i].to);
    }
    printf("%lld
",mx-mn);
}