首页 > 其他分享 >E. Unique Array

E. Unique Array

时间:2024-05-08 21:22:06浏览次数:14  
标签:unique int 修改 le 数组 Array Unique sim

E. Unique Array

You are given an integer array $a$ of length $n$. A subarray of $a$ is one of its contiguous subsequences (i. e. an array $[a_l, a_{l+1}, \dots, a_r]$ for some integers $l$ and $r$ such that $1 \le l < r \le n$). Let's call a subarray unique if there is an integer that occurs exactly once in the subarray.

You can perform the following operation any number of times (possibly zero): choose an element of the array and replace it with any integer.

Your task is to calculate the minimum number of aforementioned operation in order for all the subarrays of the array $a$ to be unique.

Input

The first line contains a single integer $t$ ($1 \le t \le 10^4$) — the number of test cases.

The first line of each test case contains a single integer $n$ ($1 \le n \le 3 \cdot 10^5$).

The second line contains $n$ integers $a_1, a_2, \dots, a_n$ ($1 \le a_i \le n$).

Additional constraint on the input: the sum of $n$ over all test cases doesn't exceed $3 \cdot 10^5$.

Output

For each test case, print a single integer — the minimum number of aforementioned operation in order for all the subarrays of the array $a$ to be unique.

Example

Input

4
3
2 1 2
4
4 4 4 4
5
3 1 2 1 2
5
1 3 2 1 2

Output

0
2
1
0

Note

In the second test case, you can replace the $1$-st and the $3$-rd element, for example, like this: $[3, 4, 1, 4]$.

In the third test case, you can replace the $4$-th element, for example, like this: $[3, 1, 2, 3, 2]$.

 

解题思路

  参考的官方题解,做法挺难想到的。

  首先如果要修改某个元素,显然应该修改成当前数组中不存在的数值,这样修改后,所有包含该元素的子数组必然是 unique 的。对此问题转换成了考虑所有不满足 unique 的子数组,最少需要修改多少个元素,使得这些子数组都至少包含一个被修改的元素。

  这就是一个经典的贪心问题:区间选点。即给定若干个区间,问最少选择多少个点使得每个区间内至少包含一个选出的点。做法是先对所有区间按右端点从小到大排序,记上一次选择的点的位置为 $j$(初始时置为 $-\infty$),依次枚举每个区间 $[l_i, r_i]$,如果 $j < l_i$ ,则选择点 $r_i$ 并更新 $j \gets r_i$,答案加 $1$。

  在本题中我们可以按照区间的右端点将不满足 unique 的子数组进行分类,依次遍历数组 $a$ 中的每个元素并维护上一个被修改元素的下标 $j$。当枚举到 $a_i$,找到非 unique 的子数组 $a_l \sim a_i$ 的最大下标 $l$,如果 $l > j$,那么修改 $a_i$ 并更新 $j \gets i$。为什么不考虑非 unique 的子数组 $a_{l'} \sim a_i$ $(l' < l)$ 呢?这是因为如果 $l \leq j$,则有 $l' < l < j$,即所有以 $i$ 为右端点的非 unique 子数组都包含了被修改的 $a_j$。否则 $l > j$,我们必须要在 $[l,i]$ 中选择一个元素 ($a_i$) 修改,此时所有以 $i$ 为右端点的非 unique 子数组也都包含了被修改的 $a_i$。

  现在的问题是对于每个 $i$,如何快速找到上述提到的 $l$?暴力的做法就是从 $i$ 往左枚举,并维护每个数值出现的次数,当发现到达某个位置时数值都出现了至少两次,那么这个位置就是 $l$。我们可以用支持区间加和查询区间最小值的线段树来加快这个过程。首先我们用另外一种方式去模拟上述过程,开一个数组 $b$,对于 $a_1 \sim a_i$ 出现的每个数值,在 $b$ 的该数值出现的最后一个位置处标记为 $1$,倒数第二个位置标记为 $-1$,其他的位置标记为 $0$。对 $b$ 求后缀和,即 $s_k = \sum\limits_{u=k}^{i}{b_u}$,如果在 $s_j \sim s_i$ 中存在某个 $s_k = 0$,说明必然有 $l > j$,此时就要修改 $a_i$。

  不妨假设在枚举完 $a_{i-1}$ 后维护出了 $s_1 \sim s_{i-1}$,此时的 $s_k = \sum\limits_{u=k}^{i-1}{b_u}$。现在枚举到 $a_i$,则需要将每个 $s_k$ 更新成 $s_k = \sum\limits_{u=k}^{i}{b_u}$。具体做法是找到数值 $a_i$ 的倒数第 $2$ 个位置 $x$ 和倒数第 $3$ 个位置 $y$,然后令 $b_y$ 从 $-1$ 变成 $0$;$b_x$ 从 $1$ 变成 $-1$;$b_i$ 变成 $1$。对应到 $s_j$ 的更新就是让 $s_1 \sim s_y$ 都加上 $1$;$s_1 \sim s_x$ 都加上 $-2$;$s_1 \sim s_i$ 都加上 $1$。然后判断 $s_j \sim s_i$ 中的最小值是否大于 $0$。因此我们需要用线段树来维护这个 $s$ 数组。

  AC 代码如下,时间复杂度为 $O(n \log{n})$:

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;

const int N = 3e5 + 5;

int a[N];
vector<int> p[N];
struct Node {
    int l, r, mn, add;
}tr[N * 4];

void build(int u, int l, int r) {
    tr[u] = {l, r, 0, 0};
    if (l != r) {
        int mid = l + r >> 1;
        build(u << 1, l, mid);
        build(u << 1 | 1, mid + 1, r);
    }
}

void pushdown(int u) {
    if (tr[u].add) {
        tr[u << 1].mn += tr[u].add;
        tr[u << 1].add += tr[u].add;
        tr[u << 1 | 1].mn += tr[u].add;
        tr[u << 1 | 1].add += tr[u].add;
        tr[u].add = 0;
    }
}

void modify(int u, int l, int r, int c) {
    if (tr[u].l >= l && tr[u].r <= r) {
        tr[u].mn += c;
        tr[u].add += c;
    }
    else {
        pushdown(u);
        int mid = tr[u].l + tr[u].r >> 1;
        if (l <= mid) modify(u << 1, l, r, c);
        if (r >= mid + 1) modify(u << 1 | 1, l, r, c);
        tr[u].mn = min(tr[u << 1].mn, tr[u << 1 | 1].mn);
    }
}

int query(int u, int l, int r) {
    if (tr[u].l >= l && tr[u].r <= r) return tr[u].mn;
    pushdown(u);
    int mid = tr[u].l + tr[u].r >> 1;
    if (r <= mid) return query(u << 1, l, r);
    if (l >= mid + 1) return query(u << 1 | 1, l, r);
    return min(query(u << 1, l, r), query(u << 1 | 1, l, r));
}

void solve() {
    int n;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%d", a + i);
        p[i].clear();
    }
    build(1, 1, n);
    int ret = 0;
    for (int i = 1, j = 0; i <= n; i++) {
        p[a[i]].push_back(i);
        int sz = p[a[i]].size();
        if (sz >= 3) modify(1, 1, p[a[i]][sz - 3], 1);
        if (sz >= 2) modify(1, 1, p[a[i]][sz - 2], -2);
        modify(1, 1, p[a[i]][sz - 1], 1);
        if (!query(1, j + 1, i)) {
            j = i;
            ret++;
        }
    }
    printf("%d\n", ret);
}

int main() {
    int t;
    scanf("%d", &t);
    while (t--) {
        solve();
    }
    
    return 0;
}

 

参考资料

  Educational Codeforces Round 165 Editorial:https://codeforces.com/blog/entry/129022

标签:unique,int,修改,le,数组,Array,Unique,sim
From: https://www.cnblogs.com/onlyblues/p/18180900

相关文章

  • Linux脚本——for循环和array数组
    #!/bin/shNodeName=(k8s-master-1k8s-master-2k8s-master-3k8s-node-1k8s-node-2k8s-node-3k8s-node-4k8s-node-5)ipv4=(100.190.110.55100.190.110.56100.190.110.57100.190.110.70100.190.110.71......
  • SystemVerilog -- 3.7 SystemVerilog 'unique' and 'priority' if-else
    SystemVerilog'unique'and'priority'if-else条件语句用于决定是否执行语句。ifelseSystemVerilog引入了一下用于违规检查的构造。ifelseunique-ifunique0-ifpriority-ifunique-if,unique0-ifunique-if按任意顺序评估条件,并执行以下操作:当所有条件都不匹配时,报......
  • SystemVerilog -- 2.8 Data Types ~ SystemVerilog Array Manipulation
    SystemVerilogArrayManipulationSystemVerilog中有许多内置方法,可帮助数组搜索和排序。数组操作方法只需循环访问数组元素,每个元素都用于计算子句指定的表达式。迭代器参数指定一个局部变量,该变量可在表达式中用于引用迭代中的当前元素。如果未提供参数,item是默认使用的名称......
  • SystemVerilog -- 2.6 Data Types ~ SystemVerilog Dynamic Arrays
    SystemVerilogDynamicArraysDynamicArrays是一个unpackedArrays,其大小可以在运行时设置或更改。因此与静态数组完全不同,静态数组的大小是在数组声明期间预先确定的。DynamicArrays的默认大小为零,直到由构造函数设置。new()SyntaxDynamicArray的尺寸由空方括号指定。[][d......
  • SystemVerilog -- 2.6 Data Types ~ SystemVerilog Unpacked Arrays
    SystemVerilogUnpackedArraysUnpackedArrays用于引用在变量名称之后声明的维度。UnpackedArrays可以是固定大小的数组、动态数组、关联数组、队列。SingleDimensionalUnpackedArraymoduletb;bytestack[8];//depth=8,1bytewidevariableinitial......
  • SystemVerilog -- 2.5 Data Types ~ SystemVerilog Packed Arrays
    SystemVerilogPackedArraysSystemVerilog中有两种类型的数组-packedarray和unpackedarray。packedarray用于引用在变量名称之前声明的维度。bit[3:0]data;//Packedarrayorvectorlogicqueue[9:0];//unpackedarraypackedarray保证表示为一组......
  • SystemVerilog -- 2.4 Data Types ~ SystemVerilog Arrays
    SystemVerilogArraysSystemVerilog在通过不同类型的数组构建复杂的数据结构方面提供了很大的灵活性。静态阵列动态阵列关联数组队列StaticArrays静态数组是指其大小在编译时间之前已知的数组。在下面显示的示例中,声明了一个8位宽的静态数组,为其分配了一些值并循环访问......
  • 查询指定用户的unique,primary索引名/键值
    --1.SQL用postgres账户查询PostgreSQL中指定DB以及schema下唯一索引的信息,按照表名:索引名:索引键值并按表名排序输出SELECTt.tablenameAStable_name,i.indexnameASindex_name,string_agg(a.attname,','ORDERBYa.attnum)ASindex_keysFROMpg_i......
  • 【Qt之JSON文件】QJsonDocument、QJsonObject、QJsonArray等类介绍及使用
    简述Qt5中包含了处理JSON的类,均以QJson开头(例如:QJsonDocument、QJsonArray、QJsonObject),在QtCore模块中,不需要额外引入其它模块。简述常用的JSON库JSON常用类简单的JSON对象简单的JSON数组复杂的JSON更多参考 常用的JSON库json.org 中介绍了......
  • C. Matching Arrays
    链接:https://codeforces.com/problemset/problem/1896/C洛谷:https://www.luogu.com.cn/problem/CF1896C这题疑似有点水了?为什么还有绿题hhhh思路:结构体+排序首先对a,b各自排序:取b的下x和a的上x比较,如果可以(指ai>bi),那么进入二阶段;如果不行,那么直接输出no。二阶段:取b的上n-x和a的......