启发式合并

启发式合并,说简单点就是把小的往大的合。

F - Colored Ball

问题陈述

N N N 个编号为 1 , 2 , … , N 1, 2, \ldots, N 1,2,,N 的盒子。最初,盒子 i i i 中有一个颜色为 C i C_i Ci 的小球。

给你 Q Q Q个查询,你要按顺序处理。

每个查询都由一对整数 ( a , b ) (a,b) (a,b) 给出,并要求您执行以下操作:

  • 将所有球从方格 a a a 移到方格 b b b,然后打印方格 b b b 中不同颜色球的数量。

这里,方格 a a a b b b 可能是空的。

思路


启发式合并板子题。
用set去维护每个集合即可,然后对每个集合进行启发式合并

#include 

using namespace std;
const int N = 1e6 + 5;
typedef long long ll;
typedef pair<ll, ll> pll;
typedef array<ll, 3> p3;
int mod = 1e9 + 7;
const int maxv = 4e6 + 5;
// #define endl "\n"
set<int> s[N];

void solve()
{
    int n,q;
    cin>>n>>q;

    for(int i=1;i<=n;i++){
        int c;
        cin>>c;
        s[i].insert(c);
    }
    while(q--){
        int a,b;
        cin>>a>>b;//把a放入b中
        if(s[a].size()<=s[b].size()){//如果a的size小于等于b,那么直接把a放入b即可
            for(auto x: s[a]){
                s[b].insert(x);
            }
            s[a].clear();
        }
        else{//不然就把b放入a中,然后最后进行交换即可。set的swap是O(1)的
            for(auto x: s[b]){
                s[a].insert(x);
            }
            s[b].clear();
            swap(s[a],s[b]);
        }
        cout<<s[b].size()<<endl;
    }
}

int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int t;
    t = 1;
    //cin>>t;
    while (t--)
    {
        solve();
    }
    system("pause");
    return 0;
}

[HNOI2009] 梦幻布丁

题目描述

n n n 个布丁摆成一行,进行 m m m 次操作。每次将某个颜色的布丁全部变成另一种颜色的,然后再询问当前一共有多少段颜色。

例如,颜色分别为 1 , 2 , 2 , 1 1,2,2,1 1,2,2,1 的四个布丁一共有 3 3 3 段颜色.

输入格式

第一行是两个整数,分别表示布丁个数 n n n 和操作次数 m m m
第二行有 n n n 个整数,第 i i i 个整数表示第 i i i 个布丁的颜色 a i a_i ai
接下来 m m m 行,每行描述一次操作。每行首先有一个整数 o p op op 表示操作类型:

  • o p = 1 op = 1 op=1,则后有两个整数 x , y x, y x,y,表示将颜色 x x x 的布丁全部变成颜色 y y y
  • o p = 2 op = 2 op=2,则表示一次询问。

输出格式

对于每次询问,输出一行一个整数表示答案。

思路

首先考虑什么时候会产生贡献,我们可以先对整个数组进行遍历,当当前颜色不等于前面的颜色时,会产生贡献。
再考虑修改的时候产生的贡献,当我们把颜色 a 改成颜色 b 时,对于颜色 a 的块 i ,当 i-1 的颜色为 b 时,贡献会减少,同理,当 i+1 的颜色为 b 时,贡献同样会减少。
对于修改操作,我们同样考虑启发式合并,即我们使用vector去维护每个颜色的布丁所在位置,同样是把个数少的布丁向个数大的布丁进行合并。
同时,我们需要记录对于颜色 i 的布丁,现在属于什么颜色。例如,我们需要把 a 变为 b,同时 a 的 size 大于 b,那么按照启发式合并的思想,我们需要把 b 放入 a 中,但我们后续要对 b 进行操作时,我们会发现 b 已经为空了,这显然是不合法的,更抽象的来说,我们并不关系每个布丁具体的颜色,即我们所说的,把 b 变为 a 和 a 变为 b 是同理的(感性理解一下),我们关心的是其对于答案的贡献。

#include 

using namespace std;
const int N = 1e6 + 5;
typedef long long ll;
typedef pair<ll, ll> pll;
typedef array<ll, 3> p3;
int mod = 1e9 + 7;
const int maxv = 4e6 + 5;
#define endl "\n"
vector<int> s[N];
vector<int> c(N),f(N);
int ans=0;
void merge(int a,int b)
{
    for(auto i : s[a]){
        if(c[i-1]==b) ans--;
        if(c[i+1]==b) ans--;
    }
    for(auto i: s[a]){
        s[b].push_back(i);
        c[i]=b;
    }
    s[a].clear();
}


void solve()
{
    int n,q;
    cin>>n>>q;
    for(int i=1;i<=n;i++){
        cin>>c[i];
        f[c[i]]=c[i];
        if(c[i]!=c[i-1]) ans++;
        s[c[i]].push_back(i);
    }
    //cout<
    while(q--){
        int op;
        cin>>op;
        if(op==1){
            int x,y;
            cin>>x>>y;
            if(x==y) continue;
            int &a=f[x],&b=f[y];
            if(s[a].size()>s[b].size()) swap(a,b);
            //把a变成b,并且a的大小大于b ,那么我们就要把a,b的所在进行交换,这样进行合并就相当于把b放入a中
            merge(a,b);
        }
        else{
            cout<<ans<<endl;
        }
    }

}

int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cout.tie(0);
    int t;
    t = 1;
    //cin>>t;
    while (t--)
    {
        solve();
    }
    system("pause");
    return 0;
}

你可能感兴趣的:(c语言,算法,c++,数据结构)