前置知识
解法
考虑 Boruvka 算法。
拆掉绝对值后得到 \(a_{i}+id,a_{i}-id,a_{j}+id,a_{j}-id\) 四个式子。
vector
启发式合并辅助线段树查询的常数过大,无法通过。上述做法的常数在于一条边会被计算两次,考虑优化。
不妨直接钦定向前连、向后连的贡献,顺次扫的过程中是容易维护的。做法正确性由最优解一定包含与其中可以保证。
代码
ll a[200010],b[200010],c[200010],f[200010];
pair<ll,ll>g[200010];
multiset<pair<ll,ll> >s;
struct DSU
{
ll fa[200010];
void init(ll n)
{
for(ll i=1;i<=n;i++)
{
fa[i]=i;
}
}
ll find(ll x)
{
return fa[x]==x?x:fa[x]=find(fa[x]);
}
void merge(ll x,ll y)
{
x=find(x);
y=find(y);
if(x!=y)
{
fa[x]=y;
}
}
}D;
ll boruvka(ll n)
{
D.init(n);
ll ans=0,flag=1,x;
while(flag==1)
{
flag=0;
fill(g+1,g+1+n,make_pair(0,0x3f3f3f3f3f3f3f3f));
s.clear();
for(ll i=1;i<=n;i++)
{
f[i]=0x3f3f3f3f3f3f3f3f;
s.insert(make_pair(f[i],i));
}
for(ll i=1;i<=n;i++)
{
x=D.find(i);
s.erase(make_pair(f[x],x));
if(s.empty()==0&&s.begin()->first+c[i]<g[x].second)
{
g[x].first=s.begin()->second;
g[x].second=s.begin()->first+c[i];
}
f[x]=min(f[x],b[i]);
s.insert(make_pair(f[x],x));
}
s.clear();
for(ll i=1;i<=n;i++)
{
f[i]=0x3f3f3f3f3f3f3f3f;
s.insert(make_pair(f[i],i));
}
for(ll i=n;i>=1;i--)
{
x=D.find(i);
s.erase(make_pair(f[x],x));
if(s.empty()==0&&s.begin()->first+b[i]<g[x].second)
{
g[x].first=s.begin()->second;
g[x].second=s.begin()->first+b[i];
}
f[x]=min(f[x],c[i]);
s.insert(make_pair(f[x],x));
}
for(ll i=1;i<=n;i++)
{
x=D.find(i);
if(g[x].first!=0&&D.find(x)!=D.find(g[x].first))
{
D.merge(x,g[x].first);
ans+=g[x].second;
flag=1;
}
}
}
return ans;
}
int main()
{
// #define Isaac
#ifdef Isaac
freopen("in.in","r",stdin);
freopen("out.out","w",stdout);
#endif
ll n,d,i;
cin>>n>>d;
for(i=1;i<=n;i++)
{
cin>>a[i];
b[i]=a[i]-i*d;
c[i]=a[i]+i*d;
}
cout<<boruvka(n)<<endl;
return 0;
}
标签:题解,ll,200010,second,Connecting,pair,Cities,id,make
From: https://www.cnblogs.com/The-Shadow-Dragon/p/18623583