并查集 D. Shark [Codeforces Round 484(Div. 2)]

一道还行的并查集,刚开始写的以为是带权并查集,写着写着发现其实不用太麻烦
题目大意是:需要找到一个值 k,使得数组中所有小于 k 的数字构成的连通块满足以下条件:

所有连通块的大小相同
连通块的数量尽可能多
在满足前两个条件下,k 的值尽可能小

如何寻找这个 k 呢?
二分显然不行,不满足单调性
那就直接从小到大枚举呗
每次枚举到一个数字,看看它前面后面是不是可以连起来
可以连起来就合并,使用并查集来维护连通块的大小
用map判断连通块大小及数量是否合格
然后扫一遍就做完了

#include<iostream>
#include<vector>
#include<map>
#include<algorithm>
using namespace std;
#define ffp(x,y,z) for(ll (x)=(y);(x)<=(z);(x++))
#define ll long long int
#define q_ read()
inline ll read()
{
    ll x = 0, f = 1;
    char ch = getchar();
    while (ch < '0' || ch>'9')
    {
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (ch >= '0' && ch <= '9')
        x = x * 10 + ch - '0', ch = getchar();
    return x * f;
}

void solve()
{
    int n = q_;
    vector<int>num(n + 2, 0);
    vector<int>id(n + 2, 0);
    vector<int>fa(n + 2, 0);
    vector<int>sz(n + 2, 0);//以i为根的子树的大小
    vector<bool>vis(n + 2, 0);
    map<int, int>ma;//判断答案用的
    ffp(i, 1, n)
    {
        num[i] = q_;
        id[i] = i;
        fa[i] = i;
        sz[i] = 1;
    }

    auto cmp = [&](int a, int b)->bool
        {
            return num[a] < num[b];
        };
    sort(id.begin() + 1, id.end() - 1, cmp);

    auto find = [&](auto&& find, int a)->int
        {
            return a == fa[a] ? a : fa[a] = find(find, fa[a]);
        };

    int ans = num[id[1]];
    int lon = 0;

    ffp(i, 1, n)
    {
        int x = id[i];//将这个位置的前面和后面连起来
        vis[x] = 1;
        sz[x] = 1;
        ma[sz[x]] += 1;
        int ba = find(find, min(x + 1, n));//找后一个,合并
        if (ba != x && vis[ba])//如果是没有出现过的数字,就别合并
        {
            ma[sz[x]]--;
            ma[sz[ba]]--;
            if (ma[sz[ba]] == 0)ma.erase(sz[ba]);
            if (ma[sz[x]] == 0)ma.erase(sz[x]);
            sz[x] += sz[ba];
            fa[ba] = x;
            ma[sz[x]]++;
            sz[ba] = 0;
        }

        int fr = find(find, max(x - 1, 1));//找前一个的根,看是谁
        if (fr != x && vis[fr])
        {//合并两根
            ma[sz[fr]] -= 1;
            ma[sz[x]] -= 1;
            if (ma[sz[fr]] == 0)ma.erase(sz[fr]);
            if (ma[sz[x]] == 0)ma.erase(sz[x]);
            sz[fr] += sz[x];
            fa[x] = fr;
            ma[sz[fr]]++;
            sz[x] = 0;
        }//记得判断是否删除了某个节点
        //合并后统计答案
        if (ma.size()==1)
        {
            for (auto [v, cnt] : ma)
            {
                if (cnt > lon)
                {
                    ans = num[x];
                    lon = cnt;
                }
            }
        }
    }
    cout << ans+1 << endl;
}


int main()
{
    int t = 1;
    while (t--)
    {
        solve();
    }
    return 0;
}
posted @ 2025-10-03 18:05  粉紫系超人气月兔铃仙  阅读(10)  评论(0)    收藏  举报