构造数组

构造数组

给定一个长度为 n 的整数数组 a1,a2,,an

请你构造长度为 n 的整数数组 b1,b2,,bn,要求数组 b 满足:

  1. b1=0
  2. 对于任意一对索引 ij (1i,jn),如果 ai=ajbi=bj(注意,如果 aiaj,则 bibj 相等与否随意)。
  3. 对于任意索引 i (i[1,n1]),要么满足 bi=bi+1,要么满足 bi+1=bi+1

请计算,一共可以构造出多少个不同的满足条件的数组 b

由于答案可能很大,你只需要输出对 998244353 取模后的结果。

例如,如果 a=[1,2,1,2,3],则一共有 2 个满足条件的数组 b,分别是 b=[0,0,0,0,0]b=[0,0,0,0,1]

输入格式

第一行包含一个整数 n

第二行包含 n 个整数 a1,a2,,an

输出格式

一个整数,表示对 998244353 取模后的结果。

数据范围

3 个测试点满足 2n5
所有测试点满足 2n2×1051ai109

输入样例1:

5
1 2 1 2 3

输出样例1:

2

输入样例2:

2
100 1

输出样例2:

2

输入样例3:

4
1 3 3 7

输出样例3:

4

 

解题思路

  如果有ai=aj,那么就有bi=bj,又因为bibj是单调递增的,即bibi+1bj,因此必然有bi=bi+1==bj。因此如果有bi=bj,那么数组b中区间[i,j]中的数都相等。

  因此最终数组b会被分成若干段,每一段中的数都相等。

  因此如果有m段,那么一共就有2m1种方案。

  因此需要把每一段区间都找出来,同时还需要把相交的区间进行合并得到一个区间,因为如果两个区间有交集,又因为每个区间中的数是一样的,因此这两个区间中的数都是一样的,因此需要把这两个区间进行合并。把剩余的区间找出来进行合并后,就会得到上图的结果。

  因此我们需要开一个哈希表来记录每一个数的出现的最左边的位置和最右边的位置,如果中这个数再次出现过,就更新最右边的位置。这题的数据故意卡了std::unordered_map,因此需要对哈希表初始化大小。

  当时写这题的时候推方案数那里推错了,导致没做出来,不过即使做出来的估计也被std::unordered_map卡了。

  AC代码如下:

复制代码
 1 #include <cstdio>
 2 #include <unordered_map>
 3 #include <algorithm>
 4 using namespace std;
 5 
 6 typedef pair<int, int> PII;
 7 
 8 const int N = 2e5 + 10, mod = 998244353;
 9 
10 unordered_map<int, PII> mp(N);  // 初始化大小
11 PII a[N];
12 int sz;
13 
14 int main() {
15     int n;
16     scanf("%d", &n);
17     for (int i = 1; i <= n; i++) {
18         int val;
19         scanf("%d", &val);
20         if (mp[val] == PII(0, 0)) mp[val] = {i, i}; // 这个数第一次出现
21         else mp[val].second = i;    // 更新右端点
22     }
23     
24     for (auto &it : mp) {
25         a[sz++] = it.second;    // 把区间都存到数组中,下面进行区间合并
26     }
27     sort(a, a + sz);
28     
29     // 区间合并过程
30     int cnt = 0;
31     for (int i = 0, l = -1, r = -1; i < sz; i++) {
32         if (a[i].first > r) {
33             cnt++;
34             l = a[i].first, r = a[i].second;
35         }
36         else {
37             r = max(r, a[i].second);
38         }
39     }
40     
41     // 方案数为2^(cnt-1)
42     int ret = 1;
43     for (int i = 0; i < cnt - 1; i++) {
44         ret = ret * 2ll % mod;
45     }
46     printf("%d", ret);
47     
48     return 0;
49 }
复制代码

  后面自己写的时候就不用std::unordered_map了,直接手写离散化,也是可以过的,AC代码如下:

复制代码
 1 #include <cstdio>
 2 #include <algorithm>
 3 using namespace std;
 4 
 5 typedef pair<int, int> PII;
 6 
 7 const int N = 2e5 + 10, mod = 998244353;
 8 
 9 int a[N], xs[N], sz;
10 PII h[N];
11 
12 int find(int x) {
13     int l = 0, r = sz - 1;
14     while (l < r) {
15         int mid = l + r >> 1;
16         if (xs[mid] >= x) r = mid;
17         else l = mid + 1;
18     }
19     
20     return l;
21 }
22 
23 int main() {
24     int n;
25     scanf("%d", &n);
26     for (int i = 1; i <= n; i++) {
27         scanf("%d", a + i);
28         xs[sz++] = a[i];
29     }
30     
31     sort(xs, xs + sz);
32     sz = unique(xs, xs + sz) - xs;
33     
34     for (int i = 1; i <= n; i++) {
35         int idx = find(a[i]);
36         if (h[idx] == PII(0, 0)) h[idx] = {i, i};
37         else h[idx].second = i;
38     }
39     
40     sort(h, h + sz);
41     
42     int cnt = 0;
43     for (int i = 0, l = -1, r = -1; i < sz; i++) {
44         if (h[i].first > r) {
45             cnt++;
46             l = h[i].first, r = h[i].second;
47         }
48         else {
49             r = max(r, h[i].second);
50         }
51     }
52     
53     int ret = 1;
54     for (int i = 0; i < cnt - 1; i++) {
55         ret = (ret << 1) % mod;
56     }
57     printf("%d", ret);
58     
59     return 0;
60
复制代码

  更新:后面发现当时没想到手写哈希表,其实手写哈希表会比离散化快很多(其实本质也是哈希映射的思想),而离散化的时间复杂度会多一个O(log n)

  手写哈希表的AC代码如下:

复制代码
 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 
 4 typedef pair<int, int> PII;
 5 
 6 const int N = 2e5 + 10, M = 1e6 + 3, mod = 998244353;
 7 
 8 int a[N];
 9 int h[M];
10 PII p[M], seg[N];
11 int sz;
12 
13 int find(int val) {
14     int t = val % M;
15     while (h[t] != -1 && h[t] != val) {
16         if (++t == M) t = 0;
17     }
18     
19     return t;
20 }
21 
22 int main() {
23     int n;
24     scanf("%d", &n);
25     
26     memset(h, -1, sizeof(h));
27     for (int i = 1; i <= n; i++) {
28         scanf("%d", a + i);
29         int t = find(a[i]);
30         if (h[t] == -1) {
31             h[t] = a[i];
32             p[t] = {i, i};
33         }
34         else {
35             p[t].second = i;
36         }
37     }
38     
39     for (int i = 1; i <= n; i++) {
40         int t = find(a[i]);
41         seg[sz++] = p[t];
42     }
43     
44     sort(seg, seg + sz);
45     
46     int cnt = 0;
47     for (int i = 0, l = -1, r = -1; i < sz; i++) {
48         if (seg[i].first > r) {
49             cnt++;
50             l = seg[i].first, r = seg[i].second;
51         }
52         else {
53             r = max(r, seg[i].second);
54         }
55     }
56     
57     int ret = 1;
58     for (int i = 0; i < cnt - 1; i++) {
59         ret = ret * 2ll % mod;
60     }
61     printf("%d", ret);
62     
63     return 0;
64 }
复制代码

 

参考资料

  AcWing 4412. 构造数组(AcWing杯 - 周赛):https://www.acwing.com/video/3836/

posted @   onlyblues  阅读(80)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
Web Analytics
点击右上角即可分享
微信分享提示