Codeforces 1340A Nastya and Strange Generator(思维)
题目大意
题目给出了一个序列,每个数字对应的位置就是第几步选择的位置。比如2 3 4 5 1,就是说第一步选了第5个位置,第二步选了第1个位置,第三步选了第3个位置。
位置选择的限制由两个数组决定,一个是\(r\)数组,一个是\(count\)数组。你可以假设原来有一个数组\(a\)里面的数是1,2,3...n,\(r\)数组代表的就是从当前这个数开始(包括自己)向右数,数组\(a\)中还没有被选择的第一个数,没有就是×,而\(count\)数组计算的是\(r\)数组中每个数出现的次数,每次都必须选一个\(count\)数组里最大的数所在位置,如果有多个可以任选一个。显然,按照上面的方法,一开始的\(r\)数组和\(a\)数组相同,为1,2,3...n,\(count\)里面都是\(1\)。
对第一个样例的解释再做一下解释。
第一步因为还没有数字被选,所以\(r\)里面有所有的数字,\(count\)里面都是\(1\),所以可以随便选,我们的样例是2 3 4 5 1,也就是说第一步选了第\(5\)个位置,于是\(5\)被拿走了,\(count[5]\)变成0。
第二步因为最大的\(5\)被拿走了,所以第\(5\)个位置右边已经没有数了,所以\(r\)数组第\(5\)个数是×,因为其他位置上的数\(count\)值\(1\),所以可以在\(1~4\)里选,根据样例,第二步选了第\(1\)个位置,于是\(1\)被拿走了,\(count[1]\)变为\(0\)。
第三步因为\(1\)被拿走了,所以\(r\)数组的第一位向右找第一个没被选的数是\(2\),\(r[1]\)就变成了\(2\),\(count[2]\)变成了\(2\)。显然现在\(count[2]\)是最大的,所以需要拿走\(2\),而样例是2 3 4 5 1,第三步正好是拿走第\(2\)个位置,所以没问题,下面几步也和这步类似。
再来看一个不太行的栗子
我们来看1 3 2这个样例(\(a\)数组是我假设出来的,方便理解)。
起初我们有\(a = [1,2,3], r = [1,2,3], count = [1,1,1]\)。因为\(count\)里面都是\(1\),换句话说每个数都是出现次数最多的数,所以可以随便选,因为样例的\(1\)在第\(1\)个位置,所以第一步选\(1\)。
第一步之后,\(a = [2,2,x], r = [2,2,3], count = [0,2,1]\)。这时候\(2\)的出现次数最多的,出现了\(2\)次,所以第二步只能选\(2\),但是样例里的\(2\)在第\(3\)个位置,也就是选了\(3\),这是矛盾的,所以这个样例不行。
具体实现
这题本质上就是一道阅读理解题,题中给的信息就是用来迷惑你的,从样例里面可以看出,每次都选末尾剩下来的最大的数,那么对于剩下来的数来说可以随便选,它们的\(count\)值不会改变,如果从它们中间或者最前面开始选,就只能从头选到不能选为止(因为它后一个数的\(count\)值会不断递增,总是会成为最大的数)。两种情况实际上本质都是选一个数字,然后往后选,选到不能选(后面的数没有/已经被选走)为止,所以我们只要检查一下给出的序列是否符合这个顺序就是了。
const int maxn = 2e5+10;
int pos[maxn]; bool vis[maxn];
int main(void) {
int t;
cin >> t;
while(t--) {
int n;
cin >> n;
for (int i = 1, num; i<=n; ++i) {
cin >> num;
pos[num] = i; vis[i] = true; //记录每一步选的位置
}
bool ok = true;
for (int i = 1; i<=n; ++i) {
if (!vis[pos[i]]) ok = false;
else {
int tmp = pos[i];
vis[tmp] = false;
while(vis[tmp+1]&&i<n) { //往后一直选到不能选
vis[++tmp]=false;
if (pos[++i]!=tmp) ok = false; //判断结果和给出的序列是否相等
}
}
}
for (int i = 1; i<=n; ++i) pos[i] = vis[i] = 0;
cout << (ok ? "Yes\n" : "No\n");
}
return 0;
}