自己好久没写题解了,写一下记录一下吧。
这场还是蛮有意思的一场,赛时一个小时不到就切完了。
A.小红的阶梯
模拟一下即可
void solve(){
int a,b,c;cin >> a >> b >> c;
cout << (a == b - 1 && b == c - 1 ? "Yes" : "No") << "\n";
}
B.小红的数组取数
由于是的目标最大化,所以选择被减数最小的数字移除,减数最大的数字移除即可。
void solve(){
int n;cin >> n;
vector<int> a(n+1),b(n+1);
for (int i = 1;i <= n;i ++) cin >> a[i];
for (int i = 1;i <= n;i ++) cin >> b[i];
cout << (min_element(a.begin()+1,a.end()) - a.begin()) << " " << (max_element(b.begin()+1,b.end()) - b.begin()) << "\n";
}
C.小红抽卡
可以发现,这题的 是没有什么用的,改变的位置永远都是
位置的值,并且每次循环右移一次。
然后由于操作具有周期性(即每 轮后与不操作
轮的数组完全相同),所以将操作数取模即可。
using i64 = long long;
void solve(){
i64 n,k,x;cin >> n >> k >> x;
vector<int> a(n);
for (int i = 0;i < n;i ++) cin >> a[i];
k %= x;
rotate(a.begin(),a.begin()+x-k,a.begin()+x);
for (int i = 0;i < n;i ++) cout << a[i] << " \n"[i==n-1];
}
D.小红的好数对
个人认为本场最难的题。
首先,我们先解剖每一步操作的本质:
设操作二元组为 ,则
,其中
表示
的数位长度。
那么现在题目要求使得 (即能被
整除)。
又因为 ,所以原式可以变为
。
所以我们可以用 表示当前数字前共有
个长度为奇数/偶数且对
取模后为
值的个数。
于是分别讨论作为二元组的 和
的情况即可。
using i64 = long long;
void solve(){
int n;
cin >> n;
vector<int> a(n+1),b(n+1);
for (int i = 1;i <= n;i ++) cin >> a[i];
for (int i = 1;i <= n;i ++) b[i] = to_string(a[i]).size() & 1,a[i] %= 11;// b[i] 用于标记当前数字位数的奇偶性
vector<vector<i64>> cnt(2,vector<i64>(11));
i64 ans = 0;
for (int i = 1;i <= n;i ++){
ans += cnt[1][a[i]];
ans += cnt[0][(11-a[i]) % 11];
if (b[i]) {
ans += cnt[0][a[i]] + cnt[1][a[i]];
} else {
ans += cnt[0][(11-a[i])%11] + cnt[1][(11-a[i])%11];
}
cnt[b[i]][a[i]] ++;
}
cout << ans << "\n";
}
E.小芳的排列构造
由于这题要从两边看,所以首先先思考一下合法 的上界值为多少,显然当其为递增/递减序列时最大,即为
,其下界为当两边为最大值,中间为最小值时,只能看到最外边两个,即
。
那么可以考虑贪心构造,将一部分移到两个最大值的外面,呈单调形式分布即可。
具体实现上,只需从大值开始枚举到小值。可以证明,只要不超过合法值域,一定存在正确解。
using i64 = long long;
void solve(){
i64 n,k;
cin >> n >> k;
if (k < 3*n - 1 || k > n + (1 + n) * n / 2) {
cout << -1 << "\n";
return;
}
vector<bool> vis(n+1);
vis[n] = vis[n-1] = 1;
k -= n*3 - 1;
for (int i = n-2;i >= 1;i --){
if (k >= i) k -= i,vis[i] = 1;
}
assert(k == 0);
for (int i = 1;i <= n-1;i ++) {
if (vis[i]) cout << i << ' ';
}
for (int i = 1;i <= n-1;i ++) {
if (!vis[i]) cout << i << " ";
}
cout << n << "\n";
}
F.小红的排列构造
题目要求构造一个 的排列,且恢复为升序排列恰好为
步。
那么同上一题一样,考虑上下界。
由于 ,所以次数下界必定为
次,否则必然不符合条件。
对于操作次数上界,显然每次都至少可以使一个元素回到正确的位置,并且当其为最后一步时,必定恰好 个元素回到正确位置,因此次数上界为
。
对于每次操作,其本质上就是不断从一个位置开始跳,每次跳到当前位置上数字的值,求回到原本出发点所需的步数。其实这个过程与交换操作是等价的,并且每次操作后的步数恰好为数字个数 。
一种实现方式:我们只需不断构造步数所需为 的数字对,直到当剩余步数
与当前剩余数字个数相等,我们就将这剩余的数字错位方式即可。
void solve(){
int n,k;
cin >> n >> k;
if (k >= n || k < (n+1) / 2) cout << -1 << '\n';
else {
vector<int> a(n);
for (int i = 0,j = n-1;i < j;i++,j --){
if (j - i == k) {
iota(a.begin()+i,a.begin()+j+1,i+1); // 递增填充函数
rotate(a.begin()+i,a.begin()+j,a.begin()+j+1);
break;
} else {
a[i] = j+1,a[j] = i+1;
k--;
}
}
for (int i = 0;i < n;i ++) cout << a[i] << " \n"[i==n-1];
}
}
好了,这样你就切掉这一场周赛了,感觉自己又摸了一天变强了。