P10032 「Cfz Round 3」Mex of Sequence
szh_AK_all · · 题解
有人说这道题不能用桶,我说:这是不可能的!
如果我们要求出一个序列中未出现过的最小自然数,那么,我们肯定要用一个同来记录那些数出现过,那些数没出现过。由于
我们用一个桶来记录每个小于等于
有如上思想后,这道题便很容易解决了。由于操作数
证明:当序列进行第一次改变后,在以后的改变中,每个元素要么变成比它大或小的数,而变成比它大或小的数后又会便成原来的数(因为第
那么,我们只要像算小数的循环节一样,算序列的循环节。为了方便起见,我们将一个序列用字符串表示,那么判断这个序列有没有出现过时便很简单了。
下面给出代码,有很多巧妙的地方还是要在代码中感悟、理解,望大家看懂。
#include <bits/stdc++.h>
using namespace std;
int a[1000005];
int tt[1000005];//tt数组为桶
int b[1000005];
string c[1000005];//记录循环
string zhuan(int x) {//将数字转换为字符串,注意字符串的顺序别反了
string p = "";
if (x == 0)
p += '0';
while (x) {
p += char(x % 10 + '0');
x /= 10;
}
string ppp = p;
for (int i = 0, j = p.size() - 1; i < p.size(); i++, j--)
ppp[j] = p[i];
return ppp;
}
int main() {
int t;
cin >> t;
while (t--) {
int n, q;
cin >> n >> q;
set<int>Q;
map<string, int>qq;
for (int i = 1; i <= n; i++) {
cin >> a[i];
if (a[i] <= n)
tt[a[i]]++;
}
for (int i = 0; i <= n; i++)
if (!tt[i])
Q.insert(i);//将没出现过的数加入set
for (int i = 1; i <= q; i++) {
for (int j = 1; j <= n; j++) {
if (a[j] <= n) {//如果将a[j]删去,a[j]这个数便没有出现时
if (tt[a[j]] == 1) {
int x;
for (int k : Q) {
x = k;
break;
}
b[j] = min(x, a[j]);
} else {
for (int k : Q) {
b[j] = k;
break;
}
}
} else {
for (int k : Q) {
b[j] = k;
break;
}
}
}
string pp = "";//序列转字符串
for (int j = 1; j <= n; j++) {
if (a[j] <= n) {
tt[a[j]]--;
if (tt[a[j]] == 0)
Q.insert(a[j]);
}
a[j] = b[j];
if (a[j] <= n) {
tt[a[j]]++;
if (tt[a[j]] == 1)
Q.erase(a[j]);
}
pp += zhuan(a[j]);
pp += " ";
}
if (qq[pp]) {//算循环节
if (i - qq[pp] == 1)
cout << c[qq[pp]];
else if ((q - qq[pp] + 1) % (i - qq[pp]) == 0)
cout << c[i - 1];
else
cout << c[qq[pp] - 1 + (q - qq[pp] + 1) % (i - qq[pp])];
for (int j = 1; j <= i; j++)
c[j] = "";
break;
}
qq[pp] = i;
c[i] = pp;
if (i == q) {
cout << pp;
for (int j = 1; j <= i; j++)
c[j] = "";
}
}
cout << endl;
memset(tt, 0, sizeof(tt));
}
}
给个赞吧! 赛时记录