Codeforces Round 887 (Div. 2)
A. Desorting
解题思路:
每次操作能使相邻数之差减\(2\),设最小相邻数之差为\(mind\),答案为\(ans = (mind + 1) / 2\)。
代码:
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<int, int>;
using pll = pair<ll, ll>;
#define fi first
#define se second
void solve()
{
int n;
cin >> n;
vector<int> v(n + 1);
for (int i = 1; i <= n; i++)
{
cin >> v[i];
}
int mind = 1e9 + 10;
for (int i = 2; i <= n; i++)
{
mind = min(mind, v[i] - v[i - 1] + 1);
if (v[i] < v[i - 1])
{
cout << 0 << endl;
return;
}
}
cout << (mind + 1) / 2 << endl;
}
int main()
{
std::ios::sync_with_stdio(0);
std::cin.tie(0);
std::cout.tie(0);
int t = 1;
cin >> t;
while (t--)
{
solve();
}
return 0;
}
B. Fibonaccharsis
解题思路:
斐波那契数列递增速度是\(logn\)级别。
\(c + b = a\),\(c = a - b\)。
不难发现,若\(a = n\),只要确定了\(b\),那么根据性质递推,整个序列就确定了。
所以第一层枚举\(n\),即枚举\(b = 1\sim n\)。第二层就直接回推,上面说了,最多回推\(logn\)次。递推过程只要最多\(logn\)次,就一定能递推到负数、推到非法或者推完跳出。
时间复杂度:\(O(nlogn)\)。
代码:
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<int, int>;
using pll = pair<ll, ll>;
#define fi first
#define se second
void solve()
{
ll n, k;
cin >> n >> k;
// c + b = a;
int ans = 0;
for (int i = 1; i <= n; i++)
{
int a = n;
int b = i;
bool f = true;
for (int j = 1; j <= k - 2; j++)
{
int c = a - b;
if (c < 0 || c > b)
{
f = false;
break;
}
a = b;
b = c;
}
if (f)
{
ans++;
}
}
cout << ans << endl;
}
int main()
{
std::ios::sync_with_stdio(0);
std::cin.tie(0);
std::cout.tie(0);
int t = 1;
cin >> t;
while (t--)
{
solve();
}
return 0;
}
C. Ntarsis' Set
解题思路:
最终答案具有单调性。
我们按第\(k\)小来看,最终答案是第\(1\)小。凡是比答案小的全部都被删完了。凡是比他大的还活着的一定是第\(i > 1\)小。
如果我们当前是第\(10\)小,删完第\(1,3,11\)小之后,这个数就是当前第\(8\)小了,因为比他小的数删了两个。
如果进行\(3\)轮,那么这个数就是第\(4\)小了。以此类推。
我们二分\(mid\),在\(check\)内对其进行\(k\)轮减去升序小于\(mid\)的个数。其中每轮寻找有\(cnt\)个要删的数的升序小于\(mid\)这一步用二分。
时间复杂度:\(O(log(1e18)\times k\times log(k))\)。
代码:
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<int, int>;
using pll = pair<ll, ll>;
#define fi first
#define se second
void solve()
{
int n, k;
cin >> n >> k;
vector<int> v(n + 1);
for (int i = 1; i <= n; i++)
{
cin >> v[i];
}
ll l = 0;
ll r = 1e18 + 10;
auto check = [&](ll mid)
{
for (int i = 1; i <= k; i++)
{
// 找到第一个大于当前位置的位置,那么前面的位置就是前进的步数
int idx = upper_bound(v.begin() + 1, v.end(), mid) - v.begin() - 1;
mid -= idx;
if (mid <= 0)
{
return false;
}
}
return true;
};
while (l + 1 < r)
{
ll mid = l + r >> 1;
if (check(mid))
{
r = mid;
}
else
{
l = mid;
}
}
cout << r << endl;
}
int main()
{
std::ios::sync_with_stdio(0);
std::cin.tie(0);
std::cout.tie(0);
int t = 1;
cin >> t;
while (t--)
{
solve();
}
return 0;
}
D. Imbalanced Arrays
解题思路:
本题思路有点复杂,建议去看洛谷题解。
为了方便构造,满足\(b_i + b_j \neq 0\),我们\(n\)个位置数的绝对值分别为\(1\sim n\).
性质一:
若\(a_i > a_j\),则\(b_i > b_j\)。
假设$b_j + b_k > 0 \(,那么因为\)b_i > b_j\(,所以一定有\)b_i + b_k >0$。
所以我们根据\(a\)进行升序排序。
此时有\(a_1 \leq a_2 \leq ...\leq a_n\),所以有\(b_1 \leq b_2 \leq ...\leq b_n\)。
对于序列\(b\)中,设绝对值最大为\(w_i\)。此时,若对应\(b_i\)为正数,那么他加上所有数都是正数。若对应\(b_i\)为负数,那么他加上任何数都是负数。
根据以上性质,我们用双指针从左右两边开始判断,即从最大最小值开始构造。我们按绝对值从大往小进行构造。
如果\(b_n + b_1 > 0\),那么\(a_n = n\)是一定的,且\(a_1 \geq 1\)也是一定的。如果我们对左边的数都进行的是负数构造,那么\(abs(b_1) < abs(b_n)\)。我们可以使\(b_n = n\)。
同理,\(a_1 = 0\)那么\(a_n = n\)是不可能的。\(a_1= 1\),那么\(a_n < n\)是不可能的。
当有一边确定当前最大绝对值符合\(a_i\)构造,那么我们就直接构造并将指针向中间移动。
当不存在左右指针的合法构造时,说明无解。
代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
using pii = pair<int, int>;
using pll = pair<ll, ll>;
#define fi first
#define se second
void solve()
{
int n;
cin >> n;
vector<pii> v(n + 1);
for (int i = 1; i <= n; i++)
{
cin >> v[i].fi;
v[i].se = i;
}
int l = 1;
int r = n;
sort(v.begin() + 1, v.end());
vector<int> ans(n + 1);
int tot = n;
while (l <= r)
{
// 绝对值最大的如果是负数,那么此处a[i] = 0;
// b[v[l].se] + {b[n - r + 1 ~ n]} > 0
// 因为 abs(b[n-r+1 ~ n]) > b[v[l].se]
// 所以如果v[l].fi == n - r,意味着当前的b[r] + b[l] 需要小于0
// 即,此时在b[l ~ r]这个区间中,b[l]的绝对值应当最大。所以赋值。
if (v[l].fi == n - r)
{
ans[v[l].se] = -tot;
l++;
tot--;
}
// 绝对值最大的如果是正数,那么此处a[i] = n;
// 此时b[1 ~ (l)]的绝对值大于b[v[r].se]。
// 所以,如果要求b[v[r].se] + b[l ~ n] > 0
// 即,v[r].fi == n - l + 1,此时在b[l ~ r]这个区间中,b[r]的绝对值应当最大。所以赋值
else if (v[r].fi == n - l + 1)
{
ans[v[r].se] = tot;
r--;
tot--;
}
else
{
cout << "NO" << endl;
return;
}
}
cout << "YES" << endl;
for (int i = 1; i <= n; i++)
{
cout << ans[i] << ' ';
}
cout << "\n";
}
int main()
{
std::ios::sync_with_stdio(0);
std::cin.tie(0);
std::cout.tie(0);
int t = 1;
cin >> t;
while (t--)
{
solve();
}
return 0;
}