题意: 给一个无向图 要使每个连通块内的点编号连续,问至少再加几条边。
用并查集和DFS写的,先DFS出各个连通块,然后把同一个连通块的元素放进一个集合,每个集合的祖先为集合中最大的元素,顺便记录其对应的最下元素。
然后开始遍历每个元素,对于每次遍历,令遍历起点为最小值,遍历终点为这个元素所在集合的最大值(会不断更新),遇到和这个元素不在一个集合的元素,则合并两个集合,同时更新最大值。
wa:
好久没写并查集 忘记初始化每个点的祖先为其本身。
#include<bits/stdc++.h>
#define IO ios::sync_with_stdio(false);cin.tie(0);cout.tie(0)
#define endl '\n'
#define for1(I, A, B) for (int I = (A); I < (B); ++I)
#define forn(I, A, B) for (int I = (A); I <= (B); ++I)
#define pb emplace_back
using namespace std;
typedef long long ll;
typedef vector<int> vi;
typedef set<int> si;
typedef double db;
const db eps=1e-8;
const db pi=acos(-1);
const ll inf=0x3f3f3f3f3f3f3f3f;
const int INF=0x3f3f3f3f;
const int MAX=1e6+10;
const ll mod2 = 1e9+7;
const ll mod=998244353;
vi g[MAX];
int v[MAX];
int maxp;
int minp;
int pre[MAX];
int find(int x)
{
int r=x;
while ( pre[r ] != r )
r=pre[r ];
int i=x , j ;
while( i != r )
{
j = pre[ i ];
pre[ i ]= r ;
i=j;
} return r ;
}
void join(int x,int y)
{
int fx=find(x),fy=find(y);
if(fx!=fy)
pre[fx]=fy;
}
void dfs(int f,int minx)
{
v[f] = 1;
join(f,minx);
maxp = max(maxp,f);
minp = min(minp,f);
for(auto x:g[f])
{
if(v[x])continue;
dfs(x,minx);
}
}
int mi[MAX];
int main()
{
int n,m;
cin>>n>>m;
forn(i,1,n)pre[i] = i;
forn(i,1,m)
{
int x,y;
cin>>x>>y;
g[x].pb(y);
g[y].pb(x);
}
int now=1;
int mark = 0;
int minn = 1;
forn(i,1,n)
{
minn = i;
if(v[i]==1)continue;
maxp = 0;
minp = INF;
dfs(minn,minn);
if(minp!=minn)cout<<"XX"<<' '<<i<<' '<<minp<<' '<<minn<<endl;
pre[find(minn)] = maxp;
pre[maxp] = maxp;
mi[maxp] = minn;
}
int ans = 0;
forn(i,1,n)
{
int nowmax = find(i);
forn(j,mi[nowmax],nowmax)
{
if(find(j) != find(nowmax))
{
int t = max(find(j),nowmax);
join(j,nowmax);
nowmax = t;
ans++;
}
}
i = nowmax;
}
cout<<ans<<endl;
}