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);
}