2024-08-04 00:01阅读: 725评论: 15推荐: 8

AtCoder Beginner Contest 365

A - Leap Year (abc365 A)

题目大意

给定年份,判断该年是否是闰年。

解题思路

根据闰年的两个条件,注意判断即可。

神奇的代码
#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 a;
cin >> a;
int day = 365;
if (a % 400 == 0 || (a % 4 == 0 && a % 100 != 0)) {
day = 366;
}
cout << day << '\n';
return 0;
}


B - Second Best (abc365 B)

题目大意

给定n个不同的数,问第二大的数的下标。

解题思路

对这n个数排序,看第二大的下标。或者两遍循环,第一次找到最大值,第二次找除最大值外的最大值。

神奇的代码
#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);
for (auto& x : a)
cin >> x;
vector<int> id(n);
iota(id.begin(), id.end(), 0);
sort(id.begin(), id.end(), [&](int i, int j) { return a[i] < a[j]; });
cout << id[n - 2] + 1 << '\n';
return 0;
}


C - Transportation Expenses (abc365 C)

题目大意

给定n个人的交通费用ai,确定最大补贴额度 x,使得给每个人的补贴为 min(x,ai), 总补贴额度不超过m

解题思路

很显然,x越高,总补贴额度越高,x越低 ,则补贴额度就越低,两者呈单调性。

因此可以二分x,然后计算补贴额度。计算补贴额度就直接for循环,求一遍 min(x,ai)即可。

神奇的代码
#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;
LL m;
cin >> n >> m;
vector<LL> a(n);
for (auto& i : a)
cin >> i;
LL l = 0, r = m + 1;
if (accumulate(a.begin(), a.end(), 0LL) <= m) {
cout << "infinite" << '\n';
} else {
auto check = [&](LL x) {
LL sum = 0;
for (auto i : a)
sum += min(i, x);
return sum <= m;
};
while (l + 1 < r) {
LL mid = (l + r) / 2;
if (check(mid))
l = mid;
else
r = mid;
}
cout << l << '\n';
}
return 0;
}


以下代码不是二分,先假定x的取值是 ai之中,对 ai从小到大遍历,找到最大的,总补贴度不超过 mai,但真正的x不一定在 ai之中,因为此时还有一些多余的钱,还可以分给每个人,只是达不到ai+1,因此剩余钱再均分给剩余人,得到真正的 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;
LL m;
cin >> n >> m;
vector<int> a(n);
for (auto& x : a)
cin >> x;
sort(a.begin(), a.end());
LL tot = accumulate(a.begin(), a.end(), 0LL);
if (tot <= m) {
cout << "infinite" << '\n';
} else {
int id = -1;
LL remain = 0;
LL presum = 0;
for (int i = 0; i < n; ++i) {
LL sum = presum + 1ll * a[i] * (n - i);
if (sum <= m) {
id = i;
remain = m - sum;
}
presum += a[i];
}
LL ans = m / n;
if (id != -1) {
ans = a[id] + remain / (n - id - 1);
}
cout << ans << '\n';
}
return 0;
}


D - AtCoder Janken 3 (abc365 D)

题目大意

剪刀石头布。

给定青木n局的操作,要求确定高桥对应的操作,要求:

  • 每一局高桥要么赢,要么平手。
  • 高桥不能连续两局出同样的手势。

最大化高桥赢的局数。

解题思路

考虑某一局高桥能做出的手势,取决于两个

  • 青木此局的手势,决定了高桥不能输
  • 高桥上一局的手势,确保不会连续两局出同样的手势

由此,为了能够作出决策,我们仅需知道第i局和上一局出的手势 j即可。

dp[i][j]表示考虑前 i局, 第i局高桥的手势是 j,高桥赢的局数的最大值。

转移则考虑高桥当前是赢还是平手(手势不与上一局相同 ),从dp[i1][]转移过来即可。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
const int inf = 1e9 + 7;
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n;
string s;
cin >> n >> s;
map<char, int> tr{{'R', 0},
{'P', 1},
{'S', 2}};
string t = "PSR";
array<int, 3> dp{0, 0, 0};
for (auto& c : s) {
array<int, 3> dp2{-inf, -inf, -inf};
int pid = tr[c];
char hashi = t[pid];
int id = tr[hashi];
for (int i = 0; i < 3; i++) {
if (i != id)
dp2[id] = max(dp2[id], dp[i] + 1);
if (i != pid)
dp2[pid] = max(dp2[pid], dp[i]);
}
dp2.swap(dp);
}
int ans = *max_element(dp.begin(), dp.end());
cout << ans << '\n';
return 0;
}


E - Xor Sigma Problem (abc365 E)

题目大意

给定n个数 ai,求i=1n1j=i+1naiai+1...aj

是异或操作。

解题思路

朴素做法显而易见的O(n2)

由于异或操作下,二进制下每一位是相互独立的,因此我们可以考虑每一位的贡献。即考虑20,21,22,...,231在这 O(n2)个异或表达式结果中出现的次数。

i=1n1j=i+1naiai+1...aj=i=0312icnti

考虑如何求cnti

单独考虑每个数二进制下的第k位,如果 2k在一个ai...aj出现过,则这说明这些数的二进制下,第k位是 1的次数是奇数。

因此对于 k,我们求 oddi表示 ai在二进制下的第 k位是不是 1。然后考虑有多少个区间odd和是奇数。

区间异或和区间加法一样,可以表示成两个前缀和相减。因此求oddi的前缀和 presumi=odd1odd2...oddi ,则oddioddi+1...oddj=presumjpresumi1

因此我们要求有多少对 (i,j),满足 presumjpresumi1=1,我们可以枚举j,然后对应的 i的数量。

  • 如果 presumj=1,则 presumi1=0
  • 如果 presumj=0,则 presumi1=1
    因此我们只需在从小到大枚举j时, 实时维护presumi=0presumi=1的数量, 然后根据presumj的取值,就知道对应的 i的数量。

得到(i,j)对数cntk后,就得到对于当前的 k,其对答案的贡献就是 2kcntk。对所有的 k累计求和即为答案。

注意上述的求法包括了 j=i的情况,而题意是 j>i,所以最后再减去所有数的和即可。

神奇的代码
#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);
for (auto& x : a)
cin >> x;
LL ans = 0;
auto solve = [&](int bit) {
vector<int> odd(n);
for (int i = 0; i < n; ++i) {
odd[i] = (a[i] >> bit) & 1;
}
for (int i = 1; i < n; ++i) {
odd[i] = odd[i] ^ odd[i - 1];
}
map<int, int> cnt;
cnt[0] = 1;
LL sum = 0;
for (int i = 0; i < n; ++i) {
sum += cnt[odd[i] ^ 1];
cnt[odd[i]]++;
}
return sum;
};
for (int i = 0; i < 32; ++i) {
ans += (1ll << i) * solve(i);
}
ans -= accumulate(a.begin(), a.end(), 0ll);
cout << ans << '\n';
return 0;
}


F - Takahashi on Grid (abc365 F)

题目大意

二维网格,n×m,每一列有一个连续的空间是空地。每一列的空地必定相交。

回答q个问题。

每个问题,给定起点终点,每次可以上下左右走一格,问移动的最小步数。

解题思路

显而易见的朴素做法的时间复杂度为O(nq),即对于每个询问,从sxsx+1sx+2...tx,一列一列移动。

以下下标(x,y)表示第x列第y行,注意与平时表示的意思不同。

考虑移动过程,其实就两个阶段:

  • 第一阶段,能够直接平行移动,即 (x,y)(x+1,y)
  • 第二阶段,不能平行移动了,得上下移动,这时一定有(x,y)(x,l[x+1])(x,r[x+1])。即抵达下一列的上下两个端点,然后往右边继续移动。

然后这两阶段不断重复。

对于第一阶段,可以通过预处理,在 O(logn)的时间内找到平行移动到最右边的边界,即此时会有 y<l[x+1]y>r[x+1] ,因此用st表预处理 lmax[i,j]表示列 [i,j]l的最大值, rmin[i,j]表示列 [i,j]r的最小值 ,通过二分可以找到这个平行移动的边界列。

然后是第二阶段,注意到第二阶段的开始起点只有2n个:每列要么是从上端点开始,要么从下端点开始。因此我们可以预处理出,从每个这样的起点出发,到达每一列的代价之类的东西。

具体是怎样的东西呢?注意到从这些端点出发,其实后续的路线就是固定了,所以可以用倍增的方法,预处理出从每个端点出发,走2i步后,抵达到哪个列的上端点或下端点,代价是多少。

这里的一步指的是必须变动y的,即经过一次第二阶段,即,如果从(x,y)能平行移动则一直平行移动,直到要上下移动才能抵达下一列s,这样就称之为一步,从xsyl[s]yr[s]

定义了一步后,就可以预处理倍增数组run[i][x][0/1]表示从 第 x行的上/下端点,走 2i步后抵达的 (列,上/下端点,移动次数)这么个三元组。

然后对于每个询问,通过二分求出第一阶段移动的移动次数,此时抵达到第二阶段的一个起点,再通过倍增数组得到移动到 (tx,ty)的移动次数。这里可能不能直接到(tx,ty),可能最后再通过第一阶段的平行移动到 tx,再上下移动到 ty,加个判断即可。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
// usage:
// auto fun = [&](int i, int j) { return min(i, j); };
// SparseTable<int, decltype(fun)> st(a, fun);
// or:
// SparseTable<int> st(a, [&](int i, int j) { return min(i, j); });
template <typename T, class F = function<T(const T&, const T&)>>
class SparseTable {
public:
int n;
vector<vector<T>> mat;
F func;
SparseTable(const vector<T>& a, const F& f) : func(f) {
n = static_cast<int>(a.size());
int max_log = 32 - __builtin_clz(n);
mat.resize(max_log);
mat[0] = a;
for (int j = 1; j < max_log; j++) {
mat[j].resize(n - (1 << j) + 1);
for (int i = 0; i <= n - (1 << j); i++) {
mat[j][i] = func(mat[j - 1][i], mat[j - 1][i + (1 << (j - 1))]);
}
}
}
T get(int from, int to) const { // [from, to]
assert(0 <= from && from <= to && to <= n - 1);
int lg = 32 - __builtin_clz(to - from + 1) - 1;
return func(mat[lg][from], mat[lg][to - (1 << lg) + 1]);
}
};
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n;
cin >> n;
vector<int> l(n), r(n);
for (int i = 0; i < n; i++)
cin >> l[i] >> r[i];
vector<vector<vector<array<LL, 3>>>> run(
31, vector<vector<array<LL, 3>>>(
n, vector<array<LL, 3>>(2, array<LL, 3>{})));
SparseTable<int> L(l, [&](int i, int j) { return max(i, j); });
SparseTable<int> R(r, [&](int i, int j) { return min(i, j); });
auto step = [&](int x, int y) {
int l = x, r = n;
while (l + 1 < r) {
int m = (l + r) / 2;
if (L.get(x, m) <= y && R.get(x, m) >= y) {
l = m;
} else {
r = m;
}
}
return l + 1;
};
for (int i = 0; i < n; ++i) {
for (int j = 0; j < 2; ++j) {
int x = i, y = (j ? r[i] : l[i]);
int nxt = step(x, y);
if (nxt == n) {
run[0][i][j] = {n, 0, n - i};
} else if (l[nxt] > y) {
run[0][i][j] = {nxt, 0, l[nxt] - y + nxt - i};
} else {
run[0][i][j] = {nxt, 1, y - r[nxt] + nxt - i};
}
}
}
for (int i = 1; i <= 30; ++i) {
for (int j = 0; j < n; ++j) {
for (int k = 0; k < 2; ++k) {
auto [nxt, d, cost] = run[i - 1][j][k];
if (nxt == n) {
run[i][j][k] = {n, 0, cost};
} else {
auto [nnxt, dd, ccost] = run[i - 1][nxt][d];
run[i][j][k] = {nnxt, dd, cost + ccost};
}
}
}
}
auto solve = [&](int sx, int sy, int tx, int ty) -> LL {
if (sx == tx) {
return abs(ty - sy);
}
if (sx > tx) {
swap(sx, tx);
swap(sy, ty);
}
int nxt = step(sx, sy);
if (nxt > tx) {
return abs(ty - sy) + tx - sx;
}
int sign = (sy < l[nxt] ? 0 : 1);
LL ret = nxt - sx + (sign ? sy - r[nxt] : l[nxt] - sy);
sx = nxt;
for (int i = 30; i >= 0; --i) {
auto [nnxt, dd, ccost] = run[i][sx][sign];
if (nnxt <= tx) {
ret += ccost;
sx = nnxt;
sign = dd;
}
}
ret += tx - sx + (sign ? abs(ty - r[sx]) : abs(l[sx] - ty));
return ret;
};
int q;
cin >> q;
while (q--) {
int sx, sy, tx, ty;
cin >> sx >> sy >> tx >> ty;
--sx, --tx;
LL ans = solve(sx, sy, tx, ty);
cout << ans << '\n';
}
return 0;
}


G - AtCoder Office (abc365 G)

题目大意

给定n个员工在公司的若干条时间段,回答 q个问题。

每个问题,询问两个员工 i,j同时在公司的时间。

解题思路

<++>

神奇的代码


本文作者:~Lanly~

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

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

posted @   ~Lanly~  阅读(725)  评论(15编辑  收藏  举报
点击右上角即可分享
微信分享提示
💬
评论
📌
收藏
💗
关注
👍
推荐
🚀
回顶
收起
  1. 1 404 not found REOL
404 not found - REOL
00:00 / 00:00
An audio error has occurred.