P8686 [蓝桥杯 2019 省 A] 修改数组

文章介绍了如何使用并查集优化算法解决数组去重问题,通过实例演示了解并查集在解决特定问题中的高效性。
摘要由CSDN通过智能技术生成

[蓝桥杯 2019 省 A] 修改数组

并查集:如果有一种循环?递归的做法,导致思维上模拟出答案的这棵树高度很大,即抛出答案往往自底向上时因为高度问题超时,那么就是并查集

并查集还可用作频繁的计算,有点类似前缀和保存计算过程的思路,来加速运算,这种计算体现在,比如我是(1,2)(2,3)(3,…)已经计算了,现在有(1,1000),但我们可以通过(999,1000)来算答案

大概意思就是这样(上一个最近算式,本轮算式)=前缀和/并查集/…保留计算过程
并查集更用于自底向上一种答案树

题目描述

给定一个长度为 N N N 的数组 A = [ A 1 , A 2 , ⋯ A N ] A=[A_1,A_2, \cdots A_N] A=[A1,A2,AN],数组中有可能有重复出现的整数。

现在小明要按以下方法将其修改为没有重复整数的数组。小明会依次修改 A 2 , A 3 , ⋯   , A N A_2,A_3, \cdots ,A_N A2,A3,,AN

当修改 A i A_i Ai 时,小明会检查 A i A_i Ai 是否在 A 1 A_1 A1 A i − 1 A_{i-1} Ai1 中出现过。如果出现过,则小明会给 A i A_i Ai 加上 1 1 1;如果新的 A i A_i Ai 仍在之前出现过,小明会持续给 A i A_i Ai 1 1 1,直到 A i A_i Ai 没有在 A 1 A_1 A1 A i − 1 A_{i-1} Ai1 中出现过。

A N A_N AN 也经过上述修改之后,显然 A A A 数组中就没有重复的整数了。

现在给定初始的 A A A 数组,请你计算出最终的 A A A 数组。

输入格式

第一行包含一个整数 N N N

第二行包含 N N N 个整数 A 1 , A 2 , ⋯   , A N A_1,A_2, \cdots ,A_N A1,A2,,AN

输出格式

输出 N N N 个整数,依次是最终的 A 1 , A 2 , ⋯   , A N A_1,A_2, \cdots ,A_N A1,A2,,AN

样例 #1

样例输入 #1

5
2 1 1 3 4

样例输出 #1

2 1 3 4 5

提示

对于 80 % 80\% 80% 的评测用例, 1 ≤ N ≤ 10000 1 \le N \le 10000 1N10000

对于所有评测用例, 1 ≤ N ≤ 1 0 5 1 \le N \le 10^5 1N105 1 ≤ A i ≤ 1 0 6 1 \le A_i \le 10^6 1Ai106

蓝桥杯 2019 年省赛 A 组 H 题。

1.哈希表做法
混分
在这里插入图片描述

#include<bits/stdc++.h>
using namespace std;
#define MAXN 1000000
int n;
bool bucket[MAXN+10];
int temp;
int main(){
  cin>>n;
  for(int i=1;i<=n;++i){
      cin>>temp;
      while(bucket[temp])++temp;
      cout<<temp<<" ";
      bucket[temp]=1;
  }
}

如果有一种循环?递归的做法,导致思维上模拟出答案的这棵树高度很大,即抛出答案往往自底向上时因为高度问题超时,那么就是并查集
在这里插入图片描述

#include <bits/stdc++.h>
using namespace std;
#define Maxn 1000000
int bcj[Maxn+10];
void init(){//并查集初始化
    for(int i=0;i<=Maxn;i++)bcj[i]=i;
}
int find(int x){//返回x所在树的根节点 
    if(x==bcj[x])return x;
    else{
        bcj[x] = find(bcj[x]);//父节点设为根节点
        return bcj[x];//返回父节点
    }
}
void merge(int i, int j){//前者并根进后者 
    bcj[find(i)] = find(j);
}
int main()
{
	init();//初始化并查集,使每个结点所在树的根初始为自己 
    int n,temp;cin>>n;
    for(int i=1;i<=n;++i){
        cin>>temp;//得到结点
        temp=find(temp);//将结点替换为所在树根节点
        cout<<temp<<" ";//输出根节点
        merge(temp,temp+1);//根节点与(根节点+1)结点并根
		//↓下一次找已有值,就输出其+1,直至没有这个值为止
		//实现方式:将其与其+1合并,使其+1为其的根节点 
    }
    return 0;
}

2 1 1 3 4
2,取2所在树的根节点,得2,输出2,合并(2,3),使2所在树根节点并根进入3所在树根节点,意味着下一次找2,就得3
1,取1所在树的根节点,得1,输出1,合并(1,2)实质为(1,3),使1所在树根结点并根进入2所在树根节点,即3所在树根节点,意味着下一次找1,就得3
1,取1所在树的根节点,得3,输出3,合并(3,4),使3所在树的根节点并根进入4所在树根节点,下一次找3,得4,同时,下一次找1,2,得4
3,…,取3所在树的根节点,得4,输出4,合并(4,5).使下一次找1,2,3,4,得5
4,…,得5
所以有:2 1 3 4 5

这,就是并查集!

  • 19
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
题目描述 如果一个数的十进制表示法中,任何相邻的两个数字均不相同,那么这个数就被称为 “有趣的数”。例如,以下各数都是有趣的数: 1、21、321、4321 但是,以下各数却不是有趣的数: 22、121、3212 给出正整数 n,请你求出长度为 n 的有趣的数的个数,并输出这个值除以 998244353 的余数。 输入格式 输入一行,包含一个整数 n。 输出格式 输出一个整数,表示答案除以 998244353 的余数。 数据范围 1≤n≤1000 输入样例1: 2 输出样例1: 81 输入样例2: 3 输出样例2: 531441 题解 有趣的数 求长度为n的有趣数的个数 思路: 第一位有9种选择,第二位有9种选择,第三位有8种选择(因为只有和前一位不一样才可以) 代码1: 时间复杂度 O(n) C++ 代码 #include <iostream> #include <cstdio> using namespace std; int main() { int n; scanf("%d", &n); int mod = 998244353; long long res = 1; for (int i = 1; i <= n; i ++ ) res = res * (i <= 2 ? 9 : 10 - i % 2) % mod; printf("%lld\n", res); return 0; } 代码2: 时间复杂度 O(n) C++ 代码 #include <iostream> #include <cstdio> using namespace std; const int MOD = 998244353; int main() { int n; scanf("%d", &n); int f[1010][10]; for (int i = 0; i < 10; i ++ ) f[1][i] = 1; for (int i = 2; i <= n; i ++ ) for (int j = 0; j < 10; j ++ ) for (int k = 0; k < 10; k ++ ) if (j != k) f[i][j] = (f[i][j] + f[i - 1][k]) % MOD; int res = 0; for (int i = 1; i < 10; i ++ ) res = (res + f[n][i]) % MOD; printf("%d\n", res); return 0; }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值