AtCoder Beginner Contest 468
https://atcoder.jp/contests/abc468/tasks
A - Maximal Value
涉及知识:语法
思路:遍历数组,找出峰值(大于左右元素)的数量即可
Code
点击查看代码
void solve()
{
cin >> n;
vi a(n + 1);
for (int i = 1; i <= n; i++)
{
cin >> a[i];
}
int res = 0;
for (int i = 1; i <= n - 2; i++)
{
if (a[i] < a[i + 1] && a[i + 1] > a[i + 2])
{
res++;
}
}
cout << res << endl;
}
B - Corridor Watch
涉及知识:语法
思路:数据范围很小,可以把所有守卫的位置存起来,然后对于每一个位置都暴力查找是否为无人看守的即可
Code
点击查看代码
void solve()
{
int d;
cin >> n >> d >> s;
vi a;
for (int i = 0; i < n; i++)
{
if (s[i] == 'G')
{
a.pb(i);
}
}
int res = 0;
for (int i = 0; i < n; i++)
{
if (s[i] == 'G')
continue;
int ok = 1;
for (int j : a)
{
if (abs(i - j) <= d)
{
ok = 0;
break;
}
}
if (ok)
res++;
}
cout << res << endl;
}
C - Between P and Q
涉及知识:全排列
思路:找出p的下一个排列,直到 \(p=q\) 或者 \(p>q\),用库函数 \(next\)_\(permutation\) 即可实现,\(vector\) 容器是可以直接比较大小的,无需手动遍历判断
Code
点击查看代码
void solve()
{
cin >> n;
vi p(n), q(n);
for (int i = 0; i < n; i++)
cin >> p[i];
for (int i = 0; i < n; i++)
cin >> q[i];
int res = 0;
while (p < q)
{
next_permutation(all(p));
if (p < q)
{
res++;
}
}
cout << res << endl;
}
D - Pre-Palindrome
涉及知识:无
思路:数据范围是 \(1e4\) 的,所以可以有 \(O(n^2)\) 的做法。先试着用暴力的想法去想,我们可以先枚举子串的长度,然后枚举起点,然后再遍历整个子串去判断要用几次操作才能成为回文串。这显然是 \(O(n^3)\) 的操作,我们想一想优化解法。如果能预先知道 \(l\) 到 \(r\) 位置的子串需要几次操作,那么我们就不需要遍历。对于长度为 \(1\) 的子串,需要操作数为 \(0\),对于长度为 \(2\) 的子串也是很容易预处理的,对于长度 \(3\) 及以上的子串,不难发现这是可以递推的,$$cnt[i][j]=cnt[i+1][j-1]+(s[i]==s[j] ? 1:0)$$。
Code
点击查看代码
void solve()
{
cin >> s;
n = s.size();
int res = 0;
vvi d(n, vi(n, 0));
for (int i = 0; i < n; i++)
{
d[i][i] = 0;
res++;
}
for (int i = 0; i < n - 1; i++)
{
if (s[i] != s[i + 1])
d[i][i + 1] = 1;
else
d[i][i + 1] = 0;
res++;
}
for (int k = 3; k <= n; k++)
{
for (int i = 0; i <= n - k; i++)
{
int j = i + k - 1;
d[i][j] = d[i + 1][j - 1];
if (s[i] != s[j])
d[i][j]++;
if (d[i][j] <= 1)
{
res++;
}
}
}
cout << res << endl;
}
E - Sum of Average
涉及知识:贡献法,前缀和,逆元
思路:数据范围是 \(1e5\) 的,无法暴力,所以考虑每个元素对结果的贡献,手模发现,每个元素对结果的贡献仅仅与元素的位置和选取子数组的长度有关系,对于第一个元素,他的权重为 \(\sum_{i=1}^{n} \dfrac{1}{i}\),对于第二个元素来说,他的权重如果直接去算就比较复杂了,我们可以用第一个元素的权重去推第二个,他们所相差的是以第二个元素为左端点的子数组,以及以第一个元素为右端点的子数组,如果我们对 \(\dfrac{1}{i}\) 取前缀和 \(p\),那么第 \(i\) 个元素为左端点的情况就应该是 \(p[n-i+1]\),减去前一个元素为右端点的子数组的情况,也就是 \(p[i-1]\),再加上前一个元素的权重即为当前元素的权重,这个题目就已经完成了
Code
点击查看代码
void solve()
{
cin >> n;
vi a(n + 1);
for (int i = 1; i <= n; i++)
{
cin >> a[i];
}
vi inv(n + 1);
vi p(n + 1);
for (int i = 1; i <= n; i++)
{
inv[i] = ksm(i, mod - 2, mod);
p[i] = (p[i - 1] + inv[i]) % mod;
}
vi b(n + 1);
for (int i = 1; i <= n; i++)
{
b[i] = ((p[n - i + 1] - p[i - 1] + mod) % mod + b[i - 1]) % mod;
}
int ans = 0;
for (int i = 1; i <= n; i++)
{
ans = (ans + b[i] * a[i]) % mod;
}
cout << ans << endl;
}
F - Chmax
涉及知识:dp,贪心
思路:假设我们现在已经用 \(x\) 吃掉了一个元素,那么如果后面有一个比 \(x\) 更大的元素,如果想要吃掉的话,必然是用 \(x\)去吃而不是 \(y\),因为如果采取后者的吃法的话, \(x\) 和 \(y\) 都会均匀飞速的增长,而前者只会让 \(x\) 不断吃掉前缀最大值,而 \(y\) 还是 \(0\),对于剩下的不是前缀最大值的元素,显然我们要的是一个递增的数组,所以只需要求出剩下元素数组的 \(LIS\) 即可,那么没被 \(x\) 吃掉,也没被选进 \(LIS\) 的元素,遍历到的时候 \(x\) 一定大于这个元素,吃掉是毫无损失的,那么\(LIS\) 的长度加上 \(x\) 吃掉元素的次数即为答案
Code
点击查看代码
void solve()
{
cin >> n;
int ans = 0;
x = 0;
vi p;
for (int i = 1; i <= n; i++)
{
cin >> y;
if (y > x)
x = y, ans++;
else
p.pb(y);
}
int res = 0;
vi a;
for (int i : p)
{
if (a.empty())
a.pb(i);
else if (a.back() < i)
a.pb(i);
else
{
auto it = lower_bound(all(a), i);
*it = i;
}
}
cout << ans + a.size() << endl;
}

浙公网安备 33010602011771号