构造数组


构造数组

给定一个长度为 $n$ 的整数数组 $a_{1},a_{2}, \dots ,a_{n}$。

请你构造长度为 $n$ 的整数数组 $b_{1},b_{2}, \dots ,b_{n}$,要求数组 $b$ 满足:

  1. $b_{1}=0$ 。
  2. 对于任意一对索引 $i$ 和 $j$ $\left( {1 \leq i,j \leq n} \right)$,如果 $a_{i}=a_{j}$ 则 $b_{i}=b_{j}$(注意,如果 $a_{i} \ne a_{j}$,则 $b_{i}$ 和 $b_{j}$ 相等与否随意)。
  3. 对于任意索引 $i$ $\left( {i \in \left[ {1,n?1} \right]} \right)$,要么满足 $b_{i}=b_{i}+1$,要么满足 $b_{i}+1=b_{i}+1$。

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

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

例如,如果 $a= \left[ {1,2,1,2,3} \right]$,则一共有 $2$ 个满足条件的数组 $b$,分别是 $b= \left[ {0,0,0,0,0} \right]$ 和 $b= \left[ {0,0,0,0,1} \right]$。

输入格式

第一行包含一个整数 $n$。

第二行包含 $n$ 个整数 $a_{1},a_{2}, \dots, a_{n}$。

输出格式

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

数据范围

前 $3$ 个测试点满足 $2 \leq n \leq 5$。
所有测试点满足 $2 \leq n \leq 2 \times {10}^{5}$,$1 \leq a_{i} \leq {10}^{9}$。

输入样例1:

5
1 2 1 2 3

输出样例1:

2

输入样例2:

2
100 1

输出样例2:

2

输入样例3:

4
1 3 3 7

输出样例3:

4

解题思路

  如果有$a_{i} = a_{j}$,那么就有$b_{i} = b_{j}$,又因为$b_{i}$到$b_{j}$是单调递增的,即$b_{i} \leq b_{i + 1} \leq \dots \leq b_{j}$,因此必然有$b_{i} = b_{i + 1} = \dots = b_{j}$。因此如果有$b_{i} = b_{j}$,那么数组$b$中区间$\left[ {i,j} \right]$中的数都相等。

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

  因此如果有$m$段,那么一共就有$2^{m-1}$种方案。

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

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

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

  AC代码如下:

 1 #include 
 2 #include 
 3 #include 
 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 
 2 #include 
 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 }

参考资料

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