并查集 - Socks - CodeForces - 731C

并查集 - Socks - CodeForces - 731C

题意:

首 行 输 入 n , m , k , 表 示 有 n 条 袜 子 , 挑 选 出 m 对 , 共 有 k 种 可 能 的 颜 色 。 首行输入n,m,k,表示有n条袜子,挑选出m对,共有k种可能的颜色。 n,m,knmk

第 二 行 输 入 n 个 数 字 c 1 , c 2 , . . . , c n , c i 表 示 第 i 条 袜 子 的 颜 色 。 第二行输入n个数字c_1,c_2,...,c_n,c_i表示第i条袜子的颜色。 nc1,c2,...,cncii

接 着 输 入 m 行 , 每 行 包 括 两 个 数 字 l 和 r , 表 示 挑 选 第 l 条 袜 子 和 第 r 条 袜 子 。 接着输入m行,每行包括两个数字l和r,表示挑选第l条袜子和第r条袜子。 mlrlr

现 在 可 以 对 袜 子 进 行 染 色 , 使 得 一 双 袜 子 的 颜 色 相 同 。 现在可以对袜子进行染色,使得一双袜子的颜色相同。 使

问 最 少 需 要 对 几 条 袜 子 染 色 , 可 以 使 得 每 一 组 的 两 条 袜 子 颜 色 相 同 。 问最少需要对几条袜子染色,可以使得每一组的两条袜子颜色相同。 使

对 所 有 的 袜 子 , 只 能 进 行 一 次 染 色 , 染 完 后 就 不 再 更 改 。 对所有的袜子,只能进行一次染色,染完后就不再更改。

Examples
Input:

3 2 3
1 2 3
1 2
2 3

Output:

2

Input:

3 2 2
1 1 2
1 2
2 1

Output:

0

数据范围:

2   ≤   n   ≤   200   000 , 0   ≤   m   ≤   200   000 , 1   ≤   k   ≤   200   000 , 1   ≤   c i   ≤   k , 1   ≤   l i ,   r i   ≤   n , l i   ≠   r i 。 T i m e   l i m i t : 2000 m s , M e m o r y   l i m i t : 262144 k B 2 ≤ n ≤ 200 000, 0 ≤ m ≤ 200 000, 1 ≤ k ≤ 200 000,\\1 ≤ c_i ≤ k,\\1 ≤ l_i, r_i ≤ n, l_i ≠ r_i。\\Time\ limit:2000 ms,Memory\ limit:262144 kB 2n200000,0m200000,1k200000,1cik,1li,rin,li=riTime limit:2000msMemory limit:262144kB


分析:

首 先 我 们 将 m 对 袜 子 两 两 之 间 连 一 条 线 , 那 么 同 一 连 通 块 中 的 袜 子 就 需 要 染 成 相 同 的 颜 色 。 首先我们将m对袜子两两之间连一条线,那么同一连通块中的袜子就需要染成相同的颜色。 m线

假 设 数 量 为 S 的 连 通 块 中 , 第 i 种 颜 色 出 现 的 次 数 最 多 , 出 现 了 s i 次 。 假设数量为S的连通块中,第i种颜色出现的次数最多,出现了s_i次。 Sisi

① 、 若 我 们 对 第 i 种 颜 色 的 所 有 袜 子 重 新 染 色 , 需 要 进 行 s i 次 操 作 , ①、若我们对第i种颜色的所有袜子重新染色,需要进行s_i次操作, isi

同 时 , 剩 下 的 所 有 袜 子 颜 色 也 都 要 改 变 成 对 应 的 颜 色 。 假 设 改 变 成 第 j 种 颜 色 。 \qquad同时,剩下的所有袜子颜色也都要改变成对应的颜色。假设改变成第j种颜色。 j

若 颜 色 j 不 在 该 连 通 块 中 , 则 总 的 操 作 次 数 就 S , 即 所 有 袜 子 颜 色 都 要 改 变 。 \qquad若颜色j不在该连通块中,则总的操作次数就S,即所有袜子颜色都要改变。 jS

若 颜 色 j 也 在 该 连 通 块 中 , 那 么 总 的 次 数 就 是 s i + ( S − s i − s j ) = S − s j 次 。 \qquad若颜色j也在该连通块中,那么总的次数就是s_i+(S-s_i-s_j)=S-s_j次。 jsi+(Ssisj)=Ssj

② 、 若 不 改 变 第 i 种 颜 色 的 袜 子 , 将 其 他 颜 色 的 袜 子 都 改 变 为 第 i 种 颜 色 , 则 总 操 作 次 数 为 S − s i 。 ②、若不改变第i种颜色的袜子,将其他颜色的袜子都改变为第i种颜色,则总操作次数为S-s_i。 iiSsi

由 于 第 i 种 颜 色 出 现 的 次 数 最 多 , 即 s i > s j , 故 第 ② 种 操 作 方 式 是 操 作 次 数 最 少 的 。 由于第i种颜色出现的次数最多,即s_i>s_j,故第②种操作方式是操作次数最少的。 isi>sj

这 也 很 好 理 解 , 要 使 得 一 堆 颜 色 相 同 , 把 除 了 出 现 次 数 最 多 的 颜 色 C i 以 外 的 其 他 颜 色 改 变 成 C i , 这 样 操 作 次 数 最 少 。 这也很好理解,要使得一堆颜色相同,把除了出现次数最多的颜色C_i以外的其他颜色改变成C_i,这样操作次数最少。 使CiCi

具体落实:

① 、 统 计 每 个 连 通 块 中 各 个 颜 色 出 现 的 次 数 , 就 需 要 先 用 并 查 集 建 立 连 通 关 系 。 ①、统计每个连通块中各个颜色出现的次数,就需要先用并查集建立连通关系。

② 、 v e c t o r 来 存 储 每 个 集 合 中 的 所 有 颜 色 , 编 号 即 为 每 个 连 通 块 的 根 节 点 的 编 号 。 ②、vector来存储每个集合中的所有颜色,编号即为每个连通块的根节点的编号。 vector

③ 、 用 m a p 来 统 计 每 个 连 通 块 中 各 个 颜 色 出 现 的 次 数 。 ③、用map来统计每个连通块中各个颜色出现的次数。 map

④ 、 用 整 个 连 通 块 元 素 的 个 数 减 去 出 现 最 频 繁 的 颜 色 出 现 的 次 数 , 就 是 该 连 通 块 需 要 操 作 的 最 少 次 数 。 ④、用整个连通块元素的个数减去出现最频繁的颜色出现的次数,就是该连通块需要操作的最少次数。

⑤ 、 对 每 个 连 通 块 求 最 少 操 作 次 数 , 最 后 求 和 即 可 。 ⑤、对每个连通块求最少操作次数,最后求和即可。

说明:

由 于 统 计 每 个 连 通 块 中 个 颜 色 出 现 的 次 数 时 , 仅 用 到 少 部 分 下 标 , 故 我 们 用 m a p , 而 不 是 开 整 型 数 组 , 再 每 次 将 数 组 置 零 , 这 样 会 超 时 。 由于统计每个连通块中个颜色出现的次数时,仅用到少部分下标,故我们用map,\\而不是开整型数组,再每次将数组置零,这样会超时。 map

代码:

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
#include<vector>

using namespace std;

const int N=2e5+10;

int n,m,k,idx;
int c[N];
int p[N];
vector<int> V[N];

int Find(int x)
{
    if(p[x]!=x) return p[x]=Find(p[x]);
    return p[x];
}

int main()
{
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=n;i++) 
    {
        scanf("%d",&c[i]);
        p[i]=i;
    }
    
    int a,b,pa,pb;
    while(m--)
    {
        scanf("%d%d",&a,&b);
        pa=Find(a),pb=Find(b);
        p[pa]=pb;
    }
    
    for(int i=1;i<=n;i++) V[Find(i)].push_back(c[i]);
    
    int res=0;
    for(int i=1;i<=n;i++)
    {
        int t=0;
        if(V[i].size())
        {
            map<int,int> cnt;
            for(int j=0;j<V[i].size();j++)
            {
                cnt[V[i][j]]++;
                t=max(t,cnt[V[i][j]]);
            }
        }
        res+=(V[i].size()-t);
    }
    
    cout<<res<<endl;
    
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值