并查集 + map (集合里元素的个数)

并查集优化算法
本文介绍了一种针对大规模数据处理的并查集算法优化方案。为避免整数溢出问题,采用一对一映射的方式将原始数据转换为较小数值,便于进行集合合并操作。通过这种方法,实现了高效地查询和维护最大集合元素数量的功能。

Invoking the Magic 20210419
ac代码:
因为输入的数据可能比较接近int最大值的一半,所以不能直接把输入的数据直接合并。
由题可知,我们只需要知道最大的一个集合元素的个数,n最大1e5,所以我们的思路就是对于每一个新的数据,都一对一映射成一个小数据,这样就能用并查集了。

#include <bits/stdc++.h>
using namespace std;
const int N = 3e5 + 9;
typedef long long ll;
unordered_map <ll,ll> m;
ll cnt;
ll n, t;
ll ans[N];
struct xxxx
{
    ll x;
    ll sum;
}bcj[N];
ll Find(ll x)
{
	ll r = x, j, k = x;
	while(bcj[r].x != r) r = bcj[r].x;
	while(k != r)
	{
		j = bcj[k].x;
		bcj[k].x = r;
		k = j;
	}
	return r;
}
void Union(ll a,ll b)
{
	a = Find(a);
	b = Find(b);
	if(a != b)// 根节点不同才加到另一个集合
    {

        bcj[b].x = a;
        bcj[a].sum += bcj[b].sum;
        // 因为是把a当根节点,所以一定要把 b 的加到 a 里面 !!!
        // 不能加反了!!!
    }
}
int main()
{
	cin >> t;
	while(t--)
	{
		cin >> n;
		cnt = 0;
		m.clear();
		memset(ans,0,sizeof(ans));
		for(ll i = 1; i <= N-9; ++i) bcj[i].x = i, bcj[i].sum = 1;
		ll a, b;
		for(ll i = 1; i <= n; ++i)
		{
			scanf("%lld %lld", &a, &b);
			if(m[a] == NULL) m[a] = ++cnt;
			a = m[a];
			if(m[b] == NULL) m[b] = ++cnt;
			b = m[b];
			Union(a, b);
		}

		for(ll i = 1; i <= cnt; ++i) Find(i);
		ll Max = 0;
		ll ttt = 0;
		for(int i = 1; i <= cnt; ++i)
        {
            ans[bcj[i].x]++;
            //cout << bcj[i].sum << "   " << bcj[i].x <<endl;
            ttt = max(ttt,bcj[i].sum);
        }

        for(int i = 1; i <= N-2; ++i)
         {
                Max = max(Max,ans[i]);
         }
        cout << ttt << endl;
        //cout << Max << endl;
        // ttt 和 Max 都是答案
	}
	return 0;
}

/*
1
3
1 2
2 3
3 1
*/

这是一个较为复杂的问题,涉及到**集合的合并(并查集)**、**统计信息的维护**以及**查询处理**。我们需要设计一个数据结构来支持: 1. **合并两个集合(Union)** 2. **查询某个集合在某些条件下的“k-权值”** --- ### 核心思路 我们采用以下策略: - 使用**并查集(Disjoint Set Union, DSU)** 来管理集合合并。 - 每个集合维护: - 所有元素的列表或哈希表(便于快速统计) - 每个 `a[i]` 出现的频率(用于计算 F(S,i)) 对于每个查询操作 `2 x l r k`,我们需要从集合 `x` 中选出满足 `l ≤ i ≤ r` 的所有元素,并基于这些元素计算 k-权值。 --- ### 实现细节 #### 数据结构设计 - 并查集:记录每个节点所属的根集合。 - 每个集合维护: - `elements`: 当前集合中所有元素的索引列表(可选) - `freq`: 一个字典,记录当前集合中每个 `a[i]` 出现的次数(即 F(S, i) 的基础) #### 查询操作实现 - 遍历集合中的所有元素,筛选出落在 `[l, r]` 区间内的。 - 对于每个 `(i, j)` 组合(包括 i=j),判断是否满足 `F(S,i)+F(S,j) <= k` - 计数即可 --- ### Python 示例代码如下: ```python from collections import defaultdict import sys input = sys.stdin.read class DSU: def __init__(self, n, a): self.parent = list(range(n)) self.groups = [set() for _ in range(n)] self.freq = [defaultdict(int) for _ in range(n)] self.a = a for i in range(n): self.groups[i].add(i) self.freq[i][a[i]] += 1 def find(self, x): while x != self.parent[x]: self.parent[x] = self.parent[self.parent[x]] x = self.parent[x] return x def union(self, x, y): xr, yr = self.find(x), self.find(y) if xr == yr: return # 合并小的到大的,优化性能 if len(self.groups[xr]) < len(self.groups[yr]): xr, yr = yr, xr # 将 yr 的内容加入 xr for elem in self.groups[yr]: self.groups[xr].add(elem) val = self.a[elem] self.freq[xr][val] += 1 # 清空 yr self.groups[yr].clear() self.freq[yr].clear() self.parent[yr] = xr def query_k_weight(self, x, l, r, k): root = self.find(x) elements = [e for e in self.groups[root] if l <= e <= r] count = 0 freq_dict = self.freq[root] for i in elements: fi = freq_dict[self.a[i]] for j in elements: fj = freq_dict[self.a[j]] if fi + fj <= k: count += 1 return count def main(): data = input().split() idx = 0 n = int(data[idx]) idx += 1 m = int(data[idx]) idx += 1 a = list(map(int, data[idx:idx+n])) idx += n dsu = DSU(n, a) res = [] for _ in range(m): op = data[idx] idx += 1 if op == '1': x = int(data[idx]) - 1 idx += 1 y = int(data[idx]) - 1 idx += 1 dsu.union(x, y) elif op == '2': x = int(data[idx]) - 1 idx += 1 l = int(data[idx]) idx += 1 r = int(data[idx]) idx += 1 k = int(data[idx]) idx += 1 ans = dsu.query_k_weight(x, l, r, k) res.append(str(ans)) print('\n'.join(res)) if __name__ == "__main__": main() ``` --- ### 解释说明 1. **初始化**: - 每个集合初始只包含自己,同时记录该集合中每个值出现的次数(对应 F(S,i)) 2. **合并操作(Union)**: - 使用路径压缩和按大小合并优化 - 合并时更新目标集合元素列表与频率统计 3. **查询操作(Query)**: - 先找到当前集合的根 - 筛选出位于区间 `[l, r]` 内的所有元素 - 双重循环遍历所有 `(i,j)`,根据频率判断是否满足条件 `F(S,i)+F(S,j) ≤ k` - 累计合法方案数 --- ### 性能分析 - 合并操作时间复杂度:接近 O(1)(路径压缩+按大小合并) - 查询操作最坏是 O(q * s^2),其中 s 是集合中符合条件的元素数量,如果 s 很大可能会超时。可以进一步优化使用前缀和/桶计数等方法提升效率。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值