2023-12-05 10:20阅读: 229评论: 0推荐: 1

AtCoder Beginner Contest 331

A - Tomorrow (abc331 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 m, d;
int Y, M, D;
cin >> m >> d >> Y >> M >> D;
++D;
if (D > d) {
D = 1;
++M;
}
if (M > m) {
M = 1;
++Y;
}
cout << Y << ' ' << M << ' ' << D << '\n';
return 0;
}


B - Buy One Carton of Milk (abc331 B)

题目大意

给定6,8,12根胡萝卜的价格。

问买至少n根胡萝卜的最小花费。

解题思路

由于n只有100,花O(n3)枚举这三类胡萝卜的购买次数,取花费最小值即可。

神奇的代码
#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, s, l;
cin >> n >> m >> s >> l;
int ans = 1e9 + 7;
for (int i = 0; i <= n; ++i)
for (int j = 0; j <= n; ++j)
for (int k = 0; k <= n; ++k) {
if (i * 6 + j * 8 + k * 12 >= n)
ans = min(ans, i * m + j * s + k * l);
}
cout << ans << '\n';
return 0;
}


C - Sum of Numbers Greater Than Me (abc331 C)

题目大意

给定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<LL> a(n);
for (auto& i : a)
cin >> i;
vector<LL> b = a;
ranges::sort(b);
vector<LL> sum(n);
partial_sum(b.begin(), b.end(), sum.begin());
LL all = accumulate(a.begin(), a.end(), 0ll);
for (auto& i : a) {
auto l = ranges::upper_bound(b, i);
LL ans = all;
if (l != b.begin())
ans -= (sum[l - b.begin() - 1]);
cout << ans << ' ';
}
cout << '\n';
return 0;
}


D - Tile Pattern (abc331 D)

题目大意

给定一个n×n的包含 BW的二维网格,将这个网格平移复制平铺在无限大的平面上。

q次询问,每次询问 [a,b][c,d] 的矩形区域的B的数量。

解题思路

直接计算该区域的B数量会有很多边界条件考虑,考虑二维前缀和,设 sum(a,b)表示 [0,0][a,b]的矩形区域的 B数量,则[a,b][c,d]=sum(c,d)sum(a1,d)sum(c,b1)+sum(a1,b1)

对于sum(a,b)的计算, 分三部分考虑:

  • 完整的n×n的矩形数量,有 an×bn个这样的完整矩形,再乘以这个矩形的B数量。
  • (a%n)×n的矩形数量,有 bn 个,再乘以这个矩形的B数量。
  • n×(b%n)的矩形数量,有 an 个,再乘以这个矩形的B数量。
  • (a%n)×(b%n) 的矩形的B数量。

求上述矩形的B的数量可以通过预处理关于B的二维前缀和O(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, q;
cin >> n >> q;
vector<string> s(n);
for (auto& i : s)
cin >> i;
vector<vector<int>> sum(n, vector<int>(n, 0));
for (int i = 0; i < n; ++i)
for (int j = 0; j < n; ++j) {
sum[i][j] += (s[i][j] == 'B');
if (i)
sum[i][j] += sum[i - 1][j];
if (j)
sum[i][j] += sum[i][j - 1];
if (i && j)
sum[i][j] -= sum[i - 1][j - 1];
}
auto solve = [&](int x, int y) {
if (x < 0 || y < 0)
return 0ll;
int row = x / n, col = y / n;
LL ans = 1ll * row * col * sum[n - 1][n - 1];
x %= n, y %= n;
ans += sum[x][y];
ans += 1ll * sum[n - 1][y] * row;
ans += 1ll * sum[x][n - 1] * col;
return ans;
};
while (q--) {
int a, b, c, d;
cin >> a >> b >> c >> d;
cout << solve(c, d) - solve(a - 1, d) - solve(c, b - 1) +
solve(a - 1, b - 1)
<< '\n';
}
return 0;
}


E - Set Meal (abc331 E)

题目大意

给定数组a和数组 b,以及l个二元组(ai,bj),要求从中各选一个数出来,但不能是(ai,bj)。问最大的和是多少。

解题思路

考虑求第k大和的做法,用优先队列维护当前的候选答案,当第k大被禁止时,就看第 k+1大,最坏情况下相当于求第 l+1大,复杂度是 O(llognm)

关于用优先队列求第K大的做法,先对两个数组降序排序,然后放入(a0+b0,0,0)。考虑我们取出的元素是 (ai+bj,i,j),当这个元素被禁止时,我们考虑它的后续答案,即 (ai+1+bj,i+1,j)(ai+bj+1,i,j+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, m, l;
cin >> n >> m >> l;
vector<int> a(n), b(m);
for (auto& i : a)
cin >> i;
for (auto& i : b)
cin >> i;
set<array<int, 2>> forbid, used;
for (int i = 0; i < l; ++i) {
int x, y;
cin >> x >> y;
--x, --y;
forbid.insert({x, y});
}
priority_queue<array<int, 3>> team;
vector<int> ida(n), idb(m);
iota(ida.begin(), ida.end(), 0);
iota(idb.begin(), idb.end(), 0);
ranges::sort(ida, [&](int x, int y) { return a[x] > a[y]; });
ranges::sort(idb, [&](int x, int y) { return b[x] > b[y]; });
team.push({a[ida[0]] + b[idb[0]], 0, 0});
used.insert({0, 0});
int ans = 0;
while (!team.empty()) {
auto [sum, x, y] = team.top();
team.pop();
if (forbid.find({ida[x], idb[y]}) == forbid.end()) {
ans = sum;
break;
}
if (x + 1 < n) {
int X = x + 1, Y = y;
if (used.find({X, Y}) == used.end()) {
team.push({a[ida[X]] + b[idb[Y]], X, Y});
used.insert({X, Y});
}
}
if (y + 1 < m) {
int X = x, Y = y + 1;
if (used.find({X, Y}) == used.end()) {
team.push({a[ida[X]] + b[idb[Y]], X, Y});
used.insert({X, Y});
}
}
}
cout << ans << '\n';
return 0;
}

也可以线性的求法,对数组b降序排序,然后对于每个 ai,找到第一个 未被禁止的 (ai,bj),作为一个候选答案。因为b是降序的,后续的 b一定是不优的。

对所有的 ai的候选答案取最大值即为答案。这样的时间复杂度是 O(n+l)


F - Palindrome Query (abc331 F)

题目大意

给定一个字符串s,进行以下两种操作:

  • 1 x csx=c
  • 2 L Rs[l..r]是否是回文串。

解题思路

判断一个串是否是回文串,可以通过比较原串和反串(即reverse,翻转)是否一致。

对原串和反串分别进行字符串hash,可以O(1)获取某一子串的hash值,通过比较原串和反串的hash是否一致,即可知道是否是回文串。

考虑到操作一的修改,由于字符串hash可合并的,用线段树维护字符串hash值即可。

线段树某一节点root,其信息为,该 root对应的子串的 hash值。两个相邻子串的hash值可以合并,得到整个串的hash值。

神奇的代码
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
const LL mo = 998244353;
const LL base = 13331;
const int N = 1e6 + 8;
LL p[N];
class segment {
#define lson root << 1
#define rson root << 1 | 1
LL hash[N << 2];
public:
void build(int root, int l, int r, string& s) {
if (l == r) {
hash[root] = (s[l - 1] - 'a');
return;
}
int mid = (l + r) >> 1;
build(lson, l, mid, s);
build(rson, mid + 1, r, s);
hash[root] = hash[lson] * p[r - mid] % mo + hash[rson];
if (hash[root] >= mo)
hash[root] -= mo;
}
void update(int root, int l, int r, int pos, int val) {
if (l == r) {
hash[root] = val;
return;
}
int mid = (l + r) >> 1;
if (pos <= mid)
update(lson, l, mid, pos, val);
else
update(rson, mid + 1, r, pos, val);
hash[root] = hash[lson] * p[r - mid] % mo + hash[rson];
if (hash[root] >= mo)
hash[root] -= mo;
}
LL get(int root, int l, int r, int L, int R) {
if (L <= l && r <= R) {
return hash[root];
}
int mid = (l + r) >> 1;
LL lval = 0, rval = 0;
if (L <= mid)
lval = get(lson, l, mid, L, R);
if (R > mid)
rval = get(rson, mid + 1, r, L, R);
return (lval * p[min(r - mid, max(0, R - mid))] % mo + rval) % mo;
}
} pre, suf;
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
p[0] = 1;
for (int i = 1; i < N; ++i)
p[i] = p[i - 1] * base % mo;
int n, q;
string s;
cin >> n >> q >> s;
pre.build(1, 1, n, s);
reverse(s.begin(), s.end());
suf.build(1, 1, n, s);
while (q--) {
int op;
cin >> op;
if (op == 1) {
int pos;
string c;
cin >> pos >> c;
pre.update(1, 1, n, pos, c[0] - 'a');
suf.update(1, 1, n, n - pos + 1, c[0] - 'a');
} else {
int l, r;
cin >> l >> r;
LL L = pre.get(1, 1, n, l, r);
LL R = suf.get(1, 1, n, (n - r + 1), (n - l + 1));
if (L == R)
cout << "Yes" << '\n';
else
cout << "No" << '\n';
}
}
return 0;
}


G - Collect Them All (abc331 G)

题目大意

给定n张卡牌,每张卡牌写的数字 [1,m]

每次抽取一张,记录卡的数字,然后放回。

问记录了这m个数字的期望抽取次数。

解题思路

<++>

神奇的代码


本文作者:~Lanly~

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

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

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