codeforces1253D DFS DSU

题意: 给一个无向图 要使每个连通块内的点编号连续,问至少再加几条边。
用并查集和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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值