2023-09-03 00:10阅读: 486评论: 2推荐: 1

AtCoder Beginner Contest 318

咕咕咕,总力战还没打,凹不过卷狗,躺了.jpg

A - Full Moon (abc318 A)

题目大意

给定n,m,p,问有多少个 i满足 0<m+pin

解题思路

减去初始的m,剩下的就是看 p的倍数个数。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, m, p;
cin >> n >> m >> p;
cout << (n - m) / p + (n >= m) << '\n';
return 0;
}


B - Overlapping sheets (abc318 B)

题目大意

一个二维空间,有n个矩形覆盖。

问有多大的空间被覆盖了。重复覆盖算一次。

解题思路

空间大小只有100,矩形只有 100,对每个矩形直接 O(1002)更新格子的覆盖情况,最后统计被覆盖的个数即可。

时间复杂度为 O(1003)

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n;
cin >> n;
array<array<int, 101>, 101> tu{};
while (n--) {
int a, b, c, d;
cin >> a >> b >> c >> d;
for (int i = a; i < b; ++i)
fill(tu[i].begin() + c, tu[i].begin() + d, 1);
}
int ans = 0;
for (auto& i : tu)
ans += count(i.begin(), i.end(), 1);
cout << ans << '\n';
return 0;
}


C - Blue Spring (abc318 C)

题目大意

n天旅行,第 i天旅行花费 fi元。

你可以购买一日游券,可以免去任意一天的旅游费用。

但是一日游券是捆绑售卖的,你需要花 p元买 d张 一日游券。这意味着你可以免去任意d天的旅游费用。你可以购买多次,最后一日游券可以剩余。

问旅游完 n天所需要的最小费用。

解题思路

假设我购买了x张一日游券,我肯定是免去旅游费用最高的那 x天的费用。

那就直接枚举买多少次一日游券,用于免去最高的旅游费用,然后其费用与剩余天数的费用相加求个最小值即可。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, d, p;
cin >> n >> d >> p;
vector<int> f(n);
for (auto& i : f)
cin >> i;
sort(f.begin(), f.end(), greater<int>());
LL sum = accumulate(f.begin(), f.end(), 0ll);
LL ans = sum;
for (int i = 0; i < n; i += d) {
for (int j = i; j < min(n, i + d); ++j)
sum -= f[j];
sum += p;
ans = min(ans, sum);
}
cout << ans << '\n';
return 0;
}


D - General Weighted Max Matching (abc318 D)

题目大意

给定一张无向图,边有边权。要求选择一些边,最大化边权和,使得每条边的顶点不重复。

解题思路

点数只有16,考虑到每条边的顶点不能重复,那么当我们考虑某条边能不能选时,必须得知道目前已经选了哪些点,以决定这条边能不能选,因此设 dp[i]表示选择的点的情况是 i(二进制压缩的状态,0表示不选,1表示选) 的最大边权和。然后枚举一条边是否选择,转移到下一个状态即可。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n;
cin >> n;
vector<vector<int>> edge(n);
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n - i - 1; ++j) {
int x;
cin >> x;
edge[i].push_back(x);
}
}
vector<LL> dp((1 << n), 0);
for (int i = 0; i < (1 << n); ++i) {
for (int j = 0; j < n; ++j) {
if ((~i >> j) & 1)
continue;
for (int k = j + 1; k < n; ++k) {
if ((~i >> k) & 1)
continue;
int w = edge[j][k - j - 1];
dp[i] = max(dp[i], dp[i ^ (1 << j) ^ (1 << k)] + w);
}
}
}
cout << *max_element(dp.begin(), dp.end()) << '\n';
return 0;
}


E - Sandwiches (abc318 E)

题目大意

给定一个n个数的数组 a,问有多少个 (i,j,k),满足

  • i<j<k
  • ai=ak
  • aiak

解题思路

由于数有105,我们只能枚举其中一个。要么枚举 i要么枚举 j。分别考虑后发现枚举 j简单一点。

如果枚举下标i会发现比较难计算,对于当前的 ai,那么对于之后的每个 ak都要考虑它们之间的其他数的个数,感觉复杂度会比较大。

考虑枚举下标j,剩下的就是考虑 j左边和 j右边的相同数。这个j对答案的贡献就是 xajlx×rx,其中 lx表示 j左边 x(是值,不是下标)的个数, rx就是右边的 x的个数。

考虑当 j移动时,只有 ajaj+1这两个数的个数发生变化。因此我们事先维护 的值,laj×raj就是此时的 j对答案的贡献。转移时通过加减那两个数的贡献,就得到 j移动到下一个数时的 的值。然后累计就是答案了。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n;
cin >> n;
vector<int> a(n);
vector<int> l(n, 0), r(n, 0);
for (auto& i : a) {
cin >> i;
--i;
r[i]++;
}
LL ans = 0;
LL tmp = 0;
for (int i = 0; i < n; ++i) {
tmp -= 1ll * l[a[i]] * r[a[i]];
r[a[i]]--;
ans += tmp;
l[a[i]]++;
tmp += 1ll * l[a[i]] * r[a[i]];
}
cout << ans << '\n';
return 0;
}


F - Octopus (abc318 F)

题目大意

给定n个宝藏的位置,以及n只长度为 li的手。你要求合法的位置的个数。

一个位置x合法,意味着你可以用n只手拿到 n个宝藏。一只手只能拿一个宝藏,且拿的范围为 [xli,x+li]

解题思路

考虑合法位置的情况,分三种,最左边,最右边,以及两个宝藏之间。

然后考虑最左边的种类,很显然,我们对手的长度进行从小到大排序,那就根据宝藏的坐标从小到大分配手。即对于第i个宝藏,分配的是长度为 li的手,那么能够及该宝藏的最左边的距离就是 xili,对该距离取个最大值,就是最左边的合法位置了。

然后考虑这个合法位置不断往右边移动,在到达第一个宝藏的位置前都是合法位置。

然后合法位置到了第一个宝藏和第二个宝藏之间。考虑在这区间移动,我们的手分配宝藏的方案是不变的,直到第二个宝藏距离当前位置比第一个位置更近时,分配方案才发生变化,需要重新计算。

注意到需要重新计算的情况,是宝藏距离当前位置的排名发生了变化,因此手分配宝藏的方案需要改变,重新计算当前位置的合法性。而排名发生变化的位置被称为关键位置

考虑这些关键位置在哪,既然是排名发生变化,那就是在某两个宝藏的中点,因此枚举左右两边宝藏,求个中点就能得到这些关键位置,注意到关键位置的数量只有O(n2)个。

对于每个关键位置,重新计算下各个宝藏到该位置的距离,然后排个序分配手,在区间[当前关键位置,下一个关键位置-1],其手的分配方案都不变,此时对于左边的宝藏,会有一个最小值 L=xi+li,而对于右边的宝藏,会有个最大值 R=xili,这之间的[R, L]就是合法位置的区间。

因此对所有这样的关键位置累计LR+1的值就是答案。总的时间复杂度是 O(n3logn)

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n;
cin >> n;
vector<LL> x(n), l(n);
for (auto& i : x)
cin >> i;
for (auto& i : l)
cin >> i;
LL ans = 0;
LL L = numeric_limits<LL>::min(), R = numeric_limits<LL>::max();
for (int i = 0; i < n; ++i) {
L = max(L, x[i] - l[i]);
R = min(R, x[i] + l[n - i - 1]);
}
ans += max(0ll, x[0] - L);
ans += max(0ll, R - x[n - 1] + 1);
vector<int> id(n);
vector<int> rk(n);
iota(id.begin(), id.end(), 0);
for (int i = 0; i < n - 1; ++i) {
vector<LL> div{x[i]};
for (int j = 0; j <= i; ++j) {
for (int k = i + 1; k < n; ++k) {
LL mid = (x[j] + x[k] + 1) / 2;
if (mid > x[i] && mid < x[i + 1])
div.push_back(mid);
}
}
sort(div.begin(), div.end());
div.push_back(x[i + 1]);
for (int k = 0; k < div.size() - 1; ++k) {
LL mid = div[k];
vector<LL> pos(n);
for (int j = 0; j < n; ++j) {
pos[j] = abs(x[j] - mid);
}
sort(id.begin(), id.end(), [&](int a, int b) {
if (pos[a] != pos[b])
return pos[a] < pos[b];
else
return a > b;
});
for (int j = 0; j < n; ++j)
rk[id[j]] = j;
L = div[k + 1] - 1, R = div[k];
for (int j = 0; j <= i; ++j) {
L = min(L, x[j] + l[rk[j]]);
}
for (int j = i + 1; j < n; ++j)
R = max(R, x[j] - l[rk[j]]);
ans += max(0ll, L - R + 1);
}
}
cout << ans << '\n';
return 0;
}



其实上面想复杂了(其实写也写复杂了,其实可以直接O(n2),枚举宝藏求中点,然后遍历所有关键点,上述这么写是因为当时考虑的每两个宝藏之间的情况,所以就一段一段的),不过是一个很朴素的从特殊位置开始的思路。

还是考虑关键位置,这个关键位置可以是两个宝藏的中点,它们的距离排名发生了变化;也可以是某个宝藏使用某只手的极限位置(即xiljxi+lj,以这些极限距离作为分割点divi,分别考虑每个分割点是否合法(同上面一样距离排序然后分配手),如果可以则代表 [divi,divi+11]都可以。可以参考jiangly的代码,但是正确性不太能理解,因为是以最优分配方式判断合法性,能保证这个区间都能取到最优方案吗(?

G - Typical Path Problem (abc318 G)

题目大意

给定一张简单无向图,问是否存在一个简单路径,从点a出发经过点 b到达点 c

解题思路

题目可以转化成是否存在一棵生成树,其根为b,点 a和点 clca是根,然后就一直在研究生成树,就不会了

神奇的代码


Ex - Count Strong Test Cases (abc318 Ex)

题目大意

<++>

解题思路

<++>

神奇的代码


本文作者:~Lanly~

本文链接:https://www.cnblogs.com/Lanly/p/17674448.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   ~Lanly~  阅读(486)  评论(2编辑  收藏  举报
历史上的今天:
2020-09-03 Codeforces Round #666 (Div. 1)
点击右上角即可分享
微信分享提示
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
收起
  1. 1 404 not found REOL
404 not found - REOL
00:00 / 00:00
An audio error has occurred.