CodeForces 915F Imbalance Value of a Tree

题意

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

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

\(\texttt{Data Range:}1\leq n\leq 10^6\)

题解

简单题。

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

如果是边权的话可以按照边权从小到大加边合并联通块。对于一条边 \((u,v,w)\) 来说,它对答案的贡献是 \(w\times sz_u\times sz_v\),用并查集维护连通块的大小即可 \(O(n\log 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\n",mx-mn);
}
posted @ 2020-10-23 10:33  Karry5307  阅读(390)  评论(1编辑  收藏  举报