洛谷 U425084 King’s Roads 题解
U425084 King’s Roads 题解
知识点
这道题是一道最小生成树 Boruvka 算法的板子题,只需略微搭配双指针与并查集。
分析
首先,明显地,它是最小生成树题目,那我们考虑 Kruscal 算法与 Prim 算法,似乎他们在完全图上作用不大,对于这道题而言,他们好像做不了(或者很难,反正我不会)。
那我们就考虑一种比较少见的最小生成树算法:Boruvka 算法。
这种算法有一个明显的优点:每次操作后联通块个数减半,总次数约为 \(\log_2{n}\) 次。
那么在这 \(\log_2{n}\) 次操作里,就足够支持我们进行时间复杂度 \(O(n)\) 的单次操作。
再者,题目中 \(a_i \in [1,m-1]\) 的条件就可以给我们一个性质:排序后,对于点 \(i\),使得边为最小值的另一个端点 \(j\) 可以用双指针维护。
但是,注意到我们还要保证边不在一个联通块上,那么双指针在更新时可能达到总时间复杂度为 \(O(n^2)\) 的情况,这如何处理呢?
我们转念一想:保证边不在一个联通块上,那么就是跳过同一个颜色的线段,我们直接再开一个并查集将相同颜色的点连起来即可,均摊时间复杂度 \(O(n\log_2{n})\)。
总时间复杂度 \(O(n\log_2{n})\)。
CODE
30pts
直接暴力建图。
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define ll long long
#define max(a,b) ((a)<(b)?(b):(a))
#define min(a,b) ((a)>(b)?(b):(a))
#define add(a,b) ((a)+(b)>=(m)?((a)+(b)-(m)):((a)+(b)<0?((a)+(b)+(m)):((a)+(b))))
#define tomax(a,b) ((a)=max((a),(b)))
#define tomin(a,b) ((a)=min((a),(b)))
#define toadd(a,b) ((a)=add((a),(b)))
#define RCL(a,b,c,d) memset((a),(b),sizeof(c)*(d))
#define FOR(i,a,b) for(register int i=(a);i<=(b);++i)
#define DOR(i,a,b) for(register int i=(a);i>=(b);--i)
#define EDGE(g,i,u,v) for(register int (i)=(g).h[(u)],(v)=(g).v[(i)];(i);(i)=(g).nxt[(i)],(v)=(g).v[(i)])
#define main Main();signed main(){ios::sync_with_stdio(0);cin.tie(0);return Main();}signed Main
using namespace std;
const int N=1e3+10,M=1e6+10;
int n,m,tot;
ll sum;
int a[N];
struct DSU{
int fa[N];
void init(int n){
FOR(i,1,n)fa[i]=i;
}
int get(int x){
return fa[x]^x?fa[x]=get(fa[x]):x;
}
bool merge(int u,int v){
int x=get(u),y=get(v);
return fa[x]=y,x!=y;
}
}dsu;
struct edge{
int u,v,w;
friend bool operator <(edge a,edge b){
return a.w<b.w;
}
}e[M];
signed main(){
cin>>n>>m;dsu.init(n);
FOR(i,1,n)cin>>a[i];
sort(a+1,a+n+1);
FOR(i,1,n)FOR(j,i+1,n)e[++tot]={i,j,add(a[i],a[j])};
sort(e+1,e+tot+1);
FOR(i,1,tot)if(dsu.merge(e[i].u,e[i].v))sum+=e[i].w;
cout<<sum<<endl;
return 0;
}
70pts
如上。
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f3f3f3f3f
#define ll long long
#define add(a,b) ((a)+(b)>=(m)?((a)+(b)-(m)):((a)+(b)))
#define RCL(a,b,c,d) memset((a),(b),sizeof(c)*(d))
#define FOR(i,a,b) for(register int i=(a);i<=(b);++i)
#define DOR(i,a,b) for(register int i=(a);i>=(b);--i)
#define main Main();signed main(){ios::sync_with_stdio(0);cin.tie(0);return Main();}signed Main
using namespace std;
const int N=1e6+10;
ll n,m,sum;
ll a[N],v[N],w[N];
int it[N][2];
struct DSU{
int cnt,fa[N];
void init(int n){
cnt=n;
FOR(i,1,n)fa[i]=i;
}
int get(int x){
return fa[x]^x?fa[x]=get(fa[x]):x;
}
bool check(int u,int v){
return get(u)==get(v);
}
bool merge(int u,int v){
int x=get(u),y=get(v);
return fa[x]=y,cnt-=(x!=y),x!=y;
}
}D[2];
signed main(){
cin>>n>>m;
FOR(i,1,n)cin>>a[i];
sort(a+1,a+n+1);
FOR(i,1,n)it[i][0]=1,it[i][1]=lower_bound(a+1,a+n+1,m-a[i])-a;
D[0].init(n),D[1].init(n);
while(D[0].cnt>1){
FOR(i,1,n-1)if(D[1].get(i)==i&&D[0].check(i,i+1))D[1].merge(i,i+1);
FOR(i,1,n)if(D[0].check(i,it[i][0]))it[i][0]=D[1].get(it[i][0])+1;
FOR(i,1,n)if(D[0].check(i,it[i][1]))it[i][1]=D[1].get(it[i][1])+1;
FOR(i,1,n)v[i]=0,w[i]=INF;
FOR(i,1,n)if(it[i][0]<=n||it[i][1]<=n){
int pa=D[0].get(i);
bool ty=(it[i][0]>n||it[i][1]>n)?(it[i][0]>n):(add(a[i],a[it[i][0]])>add(a[i],a[it[i][1]]));
if(add(a[i],a[it[i][ty]])<w[pa])w[pa]=add(a[i],a[it[i][ty]]),v[pa]=it[i][ty];
}
FOR(i,1,n)if(D[0].get(i)==i&&D[0].merge(i,v[i]))sum+=w[i];
}
cout<<sum<<endl;
return 0;
}

浙公网安备 33010602011771号