题目大意
求生成树,使得: ∑ e ∈ E T ∣ w ( e ) − e z ∣ \sum_{e\in E_T}|w(e)-e_z| ∑e∈ET∣w(e)−ez∣最小。其中 e z e_z ez表示边权的中位数
题解
和最小方差生成树没有任何本质区别。
首先来看最小方差生成树,方差可以写成平方和的平均值减去和的平方,因此可以快速计算一些数字的方差;
考虑,由于所有数字的和是 O ( n w ) O(nw) O(nw)的,所以平均值也是这个级别,因此可以枚举平均值做kruskal。
考虑优化,注意到,两条边i和j,当平均值 μ ≤ w ( i ) + w ( j ) 2 \mu\le\frac{w(i)+w(j)}{2} μ≤2w(i)+w(j)时,选i更优,否则j更优。
那么枚举这 O ( m 2 ) O(m^2) O(m2)个断点我们就知道应该保留那些边了,对这些边做kruskal即可,复杂度 O ( m 3 l g m ) O(m^3lgm) O(m3lgm)
然后继续优化,考虑枚举平均值\mu的真实含义是,考虑每条边是一个函数f(x)=(x-w_i)^2,也就是关于w(i)对称的一个二次函数,直线y=\mu从下往上依次截所有的二次函数,前n-1条被截的二次函数就是答案。
然后我们由此可以推出一个结论是,每条边存在的时间是一个区间,原因是这样的,如果 x = w i x=w_i x=wi,那么i这条边就几乎在答案中;以左端点为例,显然只有满足 w j < w i w_j<w_i wj<wi的j才有可能比i优;具体来说会在 μ ≤ w i + w j 2 \mu\le\frac{w_i+w_j}2 μ≤2wi+wj的时候j更优,那么随着 μ \mu μ的减小会有越来越多的j比i优,直到把i挤出去(也就是 u i u_i ui和 v i v_i vi连通了)为止。我们考虑构造这个左端点,对于i,按w从大到小枚举j,能加入j就加入,一直到i的两端点连通位置,那么对应的左端点就是 w i + w j 2 \frac{w_i+w_j}2 2wi+wj,同理可以求出右端点。把左右端点离散化并排序,可以得到O(m)个时间点,在左端点加入在右端点删除即可,注意并不需要维护树的形态,复杂度 O ( m 2 l g m ) O(m^2lgm) O(m2lgm)。
注意到上述求左端点的过程其实就是在维护动态最大生成树,然后左端点的询问就是找到 u i u_i ui到 v i v_i vi的边权最小边,可以用LCT维护。右端点同理,这样就可以在 O ( m l g m ) O(mlgm) O(mlgm)时间内完成这个题。
而比赛题和这个题的函数图像几乎一模一样,唯一的问题要多维护一个插入删除数求中位数的东西而已,由于任意时刻有固定个数字,可以用两个堆维护。
// luogu-judger-enable-o2
#include
#include
#include
#include
#include
#include
#define mp make_pair
#define fir first
#define sec second
#define N 200010
#define M 200010
#define lint long long
#define gc getchar()
#define INF (INT_MAX-10)
#define debug(x) cerr<<#x<<"="<
#define sp <<" "
#define ln <
using namespace std;
typedef pair<int,int> pii;
pii s[N],val[N];
int ch[N][2],fa[N],pf[N],rev[N];
inline int push_up(int x)
{ return s[x]=min(val[x],min(s[ch[x][0]],s[ch[x][1]])),0; }
inline int setc(int x,int y,int z)
{ if(!x) return fa[y]=0;ch[x][z]=y;if(y) fa[y]=x;return push_up(x); }
inline int gw(int x) { return ch[fa[x]][1]==x; }
inline int rotate(int x)
{
int y=fa[x],z=fa[y],a=gw(x),b=gw(y),c=ch[x][a^1];
return swap(pf[x],pf[y]),setc(y,c,a),setc(x,y,a^1),setc(z,x,b);
}
inline int push_down(int x)
{
if(!rev[x]) return 0;
swap(ch[x][0],ch[x][1]);
if(ch[x][0]) rev[ch[x][0]]^=1;
if(ch[x][1]) rev[ch[x][1]]^=1;
return rev[x]=0;
}
inline int all_down(int x)
{
if(fa[x]) all_down(fa[x]);
if(rev[x]) push_down(x);return 0;
}
inline int splay(int x,int tar=0)
{
all_down(x);
while(fa[x]^tar)
{
if(fa[fa[x]])
{
if(gw(x)^gw(fa[x])) rotate(x);
else rotate(fa[x]);
}
rotate(x);
}
return 0;
}
inline int expose(int x)
{
splay(x);int y=ch[x][1];if(!y) return 0;
return pf[y]=x,setc(x,0,1),fa[y]=0;
}
inline int splice(int x)
{
splay(x);int y=pf[x];if(!y) return 0;
return expose(y),setc(y,x,1),pf[x]=0,1;
}
inline int access(int x) { expose(x);while(splice(x));return splay(x); }
inline int evert(int x) { return access(x),splay(x),rev[x]^=1; }
inline int getr(int x) { access(x);while(ch[x][0]) x=ch[x][0];return splay(x),x; }
inline int con(int x,int y) { return getr(x)==getr(y); }
inline int link(int x,int y) { return evert(y),splay(y),pf[y]=x; }
inline int cut(int x,int y) { return evert(x),access(y),splay(x),setc(x,0,1),pf[y]=fa[y]=0; }
inline int query(int x,int y) { return evert(x),access(y),splay(x),s[x].sec; }
int n,m;
struct E{
int u,v,l,r,w;
inline bool operator<(const E &e)const{ return w<e.w; }
}e[M];
struct P{
int v,id,sgn;
P(int _v=0,int _id=0,int _sgn=0) { v=_v,id=_id,sgn=_sgn; }
inline bool operator<(const P &p)const{ return v<p.v; }
}p[M<<1];
struct H{
multiset<int> L,R;int Lc,Rc,c;lint Ls,Rs;
inline int init(int n)
{
return L.clear(),R.clear(),Ls=Rs=Lc=Rc=0,c=n;
}
inline int push(int x)
{
L.insert(x),Lc++,Ls+=x;int v;
if(Lc>c) R.insert(v=*(L.rbegin())),Rs+=v,Rc++,L.erase(L.find(v)),Ls-=v,Lc--;
return 0;
}
inline int pop(int x)
{
if(!Rc) return L.erase(L.find(x)),Ls-=x,Lc--;
int v=*(R.begin());
if(x>=v) R.erase(R.find(x)),Rs-=x,Rc--;
else L.erase(L.find(x)),Ls-=x,Lc--;
if(Lc<c&&Rc) v=*(R.begin()),R.erase(R.find(v)),L.insert(v),Lc++,Rc--,Ls+=v,Rs-=v;
return 0;
}
inline int size() { return Lc+Rc; }
inline lint calc(int n)
{ return Rs-Ls-(n&1)*(*(R.begin())); }
}h;
inline int inn()
{
int x,ch;while((ch=gc)<'0'||ch>'9');
x=ch^'0';while((ch=gc)>='0'&&ch<='9')
x=(x<<1)+(x<<3)+(ch^'0');return x;
}
int main()
{
n=inn(),m=inn();
for(int i=1;i<=m;i++)
e[i].u=inn(),e[i].v=inn(),e[i].w=inn();
sort(e+1,e+m+1);
for(int i=0;i<=n;i++) val[i]=s[i]=mp(INF,0);
for(int i=1;i<=m;i++) val[n+i]=s[n+i]=mp(e[i].w,i);
for(int i=1;i<=m;i++) e[i].l=-INF,e[i].r=INF;
for(int i=1;i<=m;i++)
{
int x=e[i].u,y=e[i].v,z=n+i,id;
// debug(x)sp,debug(y)sp,debug(e[i].w)ln;
if(con(x,y)) id=query(x,y),e[id].r=e[i].l=e[i].w+e[id].w,cut(id+n,e[id].u),cut(id+n,e[id].v);
link(x,z),link(y,z);
}
// for(int i=1;i<=m;i++) debug(e[i].u)sp,debug(e[i].v)sp,debug(e[i].l)sp,debug(e[i].r)ln;return 0;
int c=0;lint ans=LLONG_MAX;
for(int i=1;i<=m;i++) p[++c]=P(e[i].l,i,1),p[++c]=P(e[i].r,i,-1);
sort(p+1,p+c+1),h.init((n-1)/2);
// for(int i=1;i<=c;i++) debug(i)sp,debug(p[i].id)sp,debug(p[i].sgn)sp,debug(p[i].v)ln;
for(int i=1,j=1;i<=c;i=j)
{
while(j<=c&&p[j].v==p[i].v)
{
if(p[j].sgn>0) h.push(e[p[j].id].w);
else h.pop(e[p[j].id].w);j++;
}
if(h.size()==n-1) ans=min(ans,h.calc(n-1));
}
return !printf("%lld\n",ans);
}