2020 ICPC 沈阳 The 2020 ICPC Asia Shenyang Regional Programming Contest F. Kobolds and Catacombs.(离散化/树状数组/好题)
Kobolds are rat-like, candle-loving cave folk, digging deep beneath the surface for millennia. Today, they gather together in a queue to explore yet another tunnel in their catacombs!
But just before the glorious movement initiates, they have to arrange themselves in non-descending heights. The shortest is always the leader digging small holes, and the followers swelling it.
The kobolds are hyperactive; they like to move here and there. To make the arrangement easier, they decide to group themselves into consecutive groups first, then reorder in each group.
What's the maximum number of consecutive groups they can be partitioned into, such that after reordering the kobolds in each group in non-descending order, the entire queue is non-descending?
For example, given a queue of kobolds of heights [1, 3, 2, 7, 4], we can group them into three consecutive groups ([1] [3, 2] [7, 4]), such that after reordering each group, the entire queue can be non-descending.
Input
The first line of the input contains a single integer 𝑛n (1≤𝑛≤106), denoting the number of kobolds.
The second line contains n integers a1,a2,…,an (1≤𝑎𝑖≤109), representing the heights of the kobolds in the queue.
Output
Print a single integer, denoting the maximum number of groups.
Example
input
Copy
5
1 3 2 7 4
output
Copy
3
更新:貌似是数据水了,下面的代码能被牛客重现赛评论区的一个数据hack掉qaq
大意就是问最多将这个序列分成多少段,使得每段分别从小到大排序后整个序列是单调不减的。
只会sb解法QAQ。首先将原数组离散化,本质上就是看最多能分成多少段,使得每段是排序后单调不减的(离散化后每段是稠密的,即排序后相邻数相差不超1),同时两段之间是单调不减的。
首先对离散化后的序列进行遍历,用树状数组维护区间覆盖的情况,同时要注意几种特殊的情况。详情见代码:
#include <bits/stdc++.h>
#define int long long
//bit要用到前缀和 因此要define int long long
#define N 1000002
using namespace std;
int n, a[N];
vector<int> v;
int b[N];
void add(int x, int y) {
for(; x <= N; x += (x & -x)) b[x] += y;
}
int ask(int x) {
int ans = 0;
for(; x; x -= (x & -x)) {
ans += b[x];
}
return ans;
}
int mx_pos[N];
signed main() {
cin >> n;
for(int i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
v.push_back(a[i]);
}
//离散化
sort(v.begin(), v.end());
vector<int>::iterator it = unique(v.begin(), v.end());
v.erase(it, v.end());
for(int i = 1; i <= n; i++) {
a[i] = lower_bound(v.begin(), v.end(), a[i]) - v.begin() + 1;
mx_pos[a[i]] = i;
}
int mx = 0, premx = 0;//当前段最大值 上一个段的最大值
int i = 1;
int ans = 0;
while(i < n) {
if(ask(a[i]) - ask(a[i] - 1) == 0) add(a[i], 1);//首先进行更新
mx = max(mx, a[i]);
if(i < mx_pos[premx] && a[i] != premx) {//对应于1 1 2 3 4 1这种样例,如果当前处于i = 3的位置就应该跳过,如果处于i = 2的位置则不能跳过
//mx_pos[x]是x这个数出现的最后位置,比如上一行的样例mx_pos[1] = 6
i++;
continue;
}
int cnt = ask(mx) - ask(premx);//统计当前段最大值到上一个段最大值+1的这段区间是否被覆盖完全
if(cnt == mx - premx) {//如果已经被覆盖完全
if(i + 1 <= n && a[i + 1] < mx) {//对应于1 2 3 1这种情况如果当前i = 3,此时也算是覆盖完全,但后面的1必须算入当前段
i++;
} else {//找到了当前段的终点
ans++;
premx = mx;
mx = 0;
i++;
}
} else {//没有被覆盖完全,继续循环
i++;
}
}
cout << ans + 1;//别忘加上最后一个段(前面代码到n就停了
return 0;
}
// 7
// 1 5 2 3 3 6 4
//每一段必须是连续的 比如2 3 3 4 且5 2 3 4 2 2这样的一定要把最后的2也算进去
// 5
// 1 2 3 4 1
// 6
// 1 1 1 1 2 1