题解:CF2041N Railway Construction
前言
人类智慧题。
思路
下文中均认为按权值排序时,权值相同时编号小的更小。
先考虑如何在可接受的范围内求出最小生成树,由于图接近完全图,所以想到 Boruvka 算法。
我们可以先迭代一次,即每个点向权值最小且能连的那个点连一条边。可以发现,连通块个数是
两个连通块
i,j 之间没有边的必要条件为连通块i 内权值最小的点和连通块j 内权值最小的点无边。每两个连通块都至少需要删掉一个边,而删掉边的个数为
m ,所以连通块的量级为O(\sqrt m) 。
求一个点
接下来考虑如何把这
考虑 Prim,若我们知道了任意两个连通块之间能连的最小的边,即可在
设第
::::info[如果你被卡常了]
设枚举第
::::
此时我们就以
考虑删掉一个点过后怎么求。
对于最小生成树上的叶子节点,我们显然不需要再求一次,直接输出原来的最小生成树边权和减去叶子节点唯一连出去的一条边的权值。这条边是比较好记录的。
然后我们发现,最小生成树上的非叶节点只有
对于一个非叶节点
x ,必须存在一个y 使得y 能连的最小边端点为x 。此时排序后在
x 前面的就必须和这个y 断掉。则非叶节点最多的情况肯定是取到一个前缀,这个前缀最长就是O(\sqrt m) 。
所以我们只需要对删除非叶节点后重新跑一边暴力,复杂度为
代码
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5+5,B = 1005,inf = 2e18;
int n,m,a[N],id[N],U[N],V[N],ss[N];
inline bool cmp(int x,int y){return a[x]!=a[y]?a[x]<a[y]:x<y;}
int f[N],id2[N],du[N],sum[N];
bool vis[N];
struct node{
int u,v,w;
inline friend bool operator < (node x,node y)
{
return x.w<y.w;
}
}w[B][B],mn[B];
vector<int> vec[N],g[N],tmp[N];
vector<pair<int,int>> e[B][B];
inline int find(int x)
{
if(f[x]==x) return x;
return f[x] = find(f[x]);
}
inline int work(int ban = 0)
{
for(int i = 1;i<=n;i++) f[i] = i;
int res = 0;
for(int i = 1;i<=n;i++)
{
if(i==ban) continue;
int p = 0;
for(int j = 1;j<=n;j++)
{
if(id[j]==i||id[j]==ban)
{
if(p<g[i].size()&&g[i][p]==id[j]) p++;
continue;
}
if(p<g[i].size()&&g[i][p]==id[j])
{
p++;
continue;
}
if(find(i)!=find(id[j]))
{
f[find(i)] = find(id[j]);
res+=a[i]+a[id[j]];
if(!ban) du[i]++,du[id[j]]++,sum[i]+=a[i]+a[id[j]],sum[id[j]]+=a[i]+a[id[j]];
}
break;
}
}
int cnt = 0;
for(int i = 1;i<=n;i++)
if(i!=ban&&find(i)==i) id2[i] = ++cnt;
for(int i = 1;i<=cnt;i++)
{
vec[i].clear();
for(int j = 1;j<=cnt;j++)
e[i][j].clear();
}
for(int i = 1;i<=n;i++)
if(id[i]!=ban) vec[id2[find(id[i])]].push_back(id[i]);
for(int i = 1;i<=n;i++)
{
if(i==ban) continue;
for(auto j:g[i])
if(j!=ban) e[id2[find(i)]][id2[find(j)]].push_back({i,j});
}
for(int i = 1;i<=cnt;i++)
for(int j = i+1;j<=cnt;j++)
{
w[i][j] = {0,0,inf};
for(auto _:e[i][j]) tmp[_.first].push_back(_.second);
for(int k = 0;k<min(vec[i].size(),e[i][j].size()+1);k++)
{
int x = vec[i][k],p = 0;
for(auto y:vec[j])
{
if(a[x]+a[y]>=w[i][j].w) break;
if(p<tmp[x].size()&&tmp[x][p]==y)
{
p++;
continue;
}
w[i][j] = min(w[i][j],{x,y,a[x]+a[y]});
break;
}
}
w[j][i] = w[i][j];
for(auto _:e[i][j]) tmp[_.first].clear();
}
for(int i = 1;i<=cnt;i++) mn[i] = w[1][i],vis[i] = 0;
while(1)
{
int u = 0;
for(int i = 2;i<=cnt;i++)
if(!vis[i]&&(u==0||mn[i]<mn[u])&&mn[i].w<inf) u = i;
if(!u) break;
vis[u] = 1;
res+=mn[u].w;
if(!ban) du[mn[u].u]++,du[mn[u].v]++,sum[mn[u].u]+=mn[u].w,sum[mn[u].v]+=mn[u].w;
for(int i = 2;i<=cnt;i++)
if(!vis[i]) mn[i] = min(mn[i],w[u][i]);
}
for(int i = 2;i<=cnt;i++)
if(!vis[i]) return -1;
return res;
}
signed main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>n>>m;
for(int i = 1;i<=n;i++)
cin>>a[i],id[i] = i;
for(int i = 1;i<=m;i++)
cin>>U[i]>>V[i],g[U[i]].push_back(V[i]),g[V[i]].push_back(U[i]),ss[U[i]]++,ss[V[i]]++;
sort(id+1,id+n+1,cmp);
for(int i = 1;i<=n;i++) sort(g[i].begin(),g[i].end(),cmp);
int tmp = work();
if(tmp==-1)
{
if(n==2) cout<<"0\n0";
else
{
int p = 1;
for(int i = 1;i<=n;i++)
if(ss[i]==n-1) p = i;
int res = work(p);
for(int i = 1;i<=n;i++)
if(i==p) cout<<res<<' ';
else cout<<"-1 ";
}
return 0;
}
for(int i = 1;i<=n;i++)
if(du[i]==1) cout<<tmp-sum[i]<<' ';
else cout<<work(i)<<' ';
return 0;
}