CF1450D 排名压缩
1 CF1450D 排名压缩
2 题目描述
在竞赛编程平台 \(CodeCook\) 上,每个人都有一个由长度为 \(n\) 的整数数组 \(a\) 表示的评级图。现在要更新系统,需要写一个程序来压缩这些评级图。
程序是这么工作的,给定一个整数参数 \(𝑘\),程序取 \(𝑎\) 中每个长度为 \(𝑘\) 的连续子数组的最小值。
更具体地说,对于长度为 \(𝑛\) 的数组 \(𝑎\) 和整数 \(𝑘\),将 \(𝑎\) 的 \(𝑘\) 压缩数组定义为长度为 \(n-k+1\) 的数组 \(𝑏\),这样
\(𝑏_𝑗=\underset{j≤i≤j+k-1}{min}𝑎_𝑖\)
例如,\([1,3,4,5,2]\) 的 \(3\) 压缩数组是 \([min\){1,3,4}\(,min\){3,4,5}\(,min\){4,5,2}\(]=[1,3,2]\)。
长度 \(𝑚\) 的排列是一个由 \(𝑚\) 个从 \(1\) 到 \(𝑚\) 的不同整数按任意顺序组成的数组。例如,\([2,3,1,5,4]\) 是置换,但 \([1,2,2]\) 不是置换(\(2\) 在数组中出现了两次),\([1,3,4]\) 也不是置换(\(𝑚=3\) 但数组中有 \(4\))。
一个 \(𝑘\) 压缩数组如果是一个全排列序列,\(CodeCook\) 的用户会很高兴。给定一个数组 \(𝑎\),确定所有 \(1≤𝑘≤𝑛\) 的 \(CodeCook\) 用户是否会在此数组的 \(𝑘\) 压缩后感到高兴。
3 解题思路
我们仔细分析性质:在第 \(k\) 种压缩中,如果我们想要满足整个序列为全排列序列,那么这个全排列中涉及的数是 \(1\) 到 \(n-k+1\)。然后我们发现,一个序列想要满足全排列,必须满足以下性质:最小的数只能出现一次且必须出现在边界。否则这个数就会被重复计算,导致答案不是全排列形式。而且通过分析样例,我们得到:如果第 \(k\) 种压缩是全排列形式,那么所有大于 \(k\) 的排列也都是全排列形式。这是因为在 \(k\) 的增大过程中,压缩序列中的数越来越少。如果第 \(k\) 种压缩是全排列方式,那么说明最小的数都在边缘,向中间的数逐渐增大。如果我们在这时给所有的子序列长度都 \(+1\),那么原本的最小数不会消失,反而是最大的数被次大的数覆盖了。
于是我们得到算法:\(i\)(数字)从 \(1\) 枚举到 \(n\),每次看它是否存在。如果存在,那么第 \(n-i+1\) 种压缩成立,否则直接跳出。接下来看如果最小值的个数大于 \(1\) 或者最小值的位置不在边缘,那么下一次便不成立了,跳出。
注意要特判 \(1\) 和 \(n\) 的情况。
4 代码(空格警告):
#include <iostream>
#include <deque>
using namespace std;
const int N = 3e5+10;
int T, n, flag, flag2, k;
int a[N], ans[N], cnt[N];
deque<int> q;
int main()
{
cin >> T;
while (T--)
{
while (q.size()) q.pop_back();
flag = 1;
flag2 = 0;
cin >> n;
for (int i = 1; i <= n; i++) cnt[i] = 0, ans[i] = 0;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
if (a[i] == 1) flag2 = 1;
if (cnt[a[i]]) flag = 0;
cnt[a[i]]++;
q.push_back(a[i]);
}
for (int i = 1; i <= n; i++)
{
k = n - i + 1;
if (!cnt[i]) break;
cnt[i]--;
ans[k] = 1;
if (cnt[i]) break;
if (q.front() == i) q.pop_front();
else if (q.back() == i) q.pop_back();
else break;
}
ans[1] = flag;
ans[n] = flag2;
for (int i = 1; i <= n; i++) cout << ans[i];
puts("");
}
return 0;
}