Atcoder Beginner Contest 331 A~F
A.Tomorrow(模拟)
题意:
有一本日历,一年有 M M M个月,一个月有 D D D天,现在给你一个日期,给出的形式是 X X XX XX年, X X XX XX月, X X XX XX日。问这个日期后面一天是哪一天。
分析:
按照要求模拟即可。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int main() {
int t = 1;
while (t--) {
int M, D;
cin >> M >> D;
int y, m, d;
cin >> y >> m >> d;
d++;
if (d > D)
d = 1, m++;
if (m > M)
m = 1, y++;
cout << y << ' ' << m << ' ' << d << endl;
}
return 0;
}
B.Buy One Carton of Milk(枚举)
题意:
6 6 6个鸡蛋 S S S元, 8 8 8个鸡蛋 M M M元, 12 12 12个鸡蛋 L L L元,问至少买 N N N个鸡蛋需要多少钱。
分析:
分别枚举买 6 6 6个, 8 8 8个, 12 12 12个一包的鸡蛋的数量,取最小值即可。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int main() {
int n, s, m, l;
cin >> n >> s >> m >> l;
int ans = 1e9;
for (int a = 0; a <= n; a++) {
for (int b = 0; b <= n; b++) {
for (int c = 0; c <= n; c++) {
if (a * 6 + b * 8 + c * 12 >= n) {
ans = min(ans, a * s + b * m + c * l);
}
}
}
}
cout << ans << endl;
return 0;
}
C.Sum of Numbers Greater Than Me(后缀和)
题意:
给出一个 a a a序列,对于 a i a_i ai,问大于 a i a_i ai的所有元素之和是多少。
分析:
因为 a i ≤ 1 0 6 a_i\le10^6 ai≤106,我们从大到小枚举 i i i, s u m [ i ] sum[i] sum[i]表示大于等于 a i a_i ai的数字之和。先将 a i a_i ai出现次数记录到 s u m [ a i ] sum[a_i] sum[ai]中。从大到小递推可得 s u m [ i ] = s u m [ i + 1 ] + i ∗ s u m [ i ] sum[i]=sum[i+1]+i*sum[i] sum[i]=sum[i+1]+i∗sum[i]
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e6 + 10;
LL sum[N], a[N];
int main() {
int n;
cin >> n;
for (int i = 0; i < n; i++) {
cin >> a[i];
sum[a[i]]++;
}
for (int i = 1000000; i >= 1; i--)
sum[i] = sum[i + 1] + sum[i] * i;
for (int i = 0; i < n; i++)
cout << sum[a[i] + 1] << ' ';
return 0;
}
D Tile Pattern(二维前缀和)
题意:
给一张 1 0 9 ∗ 1 0 9 10^9*10^9 109∗109的网格图, P [ i ] [ j ] P[i][j] P[i][j]表示第 i i i行,第 j j j列格子的颜色,网格图剩下格子的颜色为 P [ i % n ] [ j % n ] P[i\%n][j\%n] P[i%n][j%n]。询问给出矩形左上角的坐标 ( A , B ) (A,B) (A,B),右下角的坐标 ( C , D ) (C,D) (C,D),输出矩形内黑格子的数量。
分析:
先预处理
n
∗
n
n∗n
n∗n 的范围内的二维前缀和。记以
(
0
,
0
)
(0,0)
(0,0) 为左上角,
(
x
,
y
)
(x,y)
(x,y) 为右下角的黑格子的数量为
f
(
x
,
y
)
f(x,y)
f(x,y)
对于一整个区域,我们分为四部分:
- 左上角的 ( x / n ) ∗ ( y / n ) (x/n)∗(y/n) (x/n)∗(y/n) 个 n ∗ n n∗n n∗n 的范围
- 右上角的 ( x / n ) (x/n) (x/n) 个 n ∗ ( y % n ) n∗(y\%n) n∗(y%n) 的范围
- 左下角的 ( y / n ) (y/n) (y/n) 个 n ∗ ( x % n ) n∗(x\%n) n∗(x%n) 的范围
- 右下角的 1 个 ( x % n ) ∗ ( y % n ) (x\%n)∗(y\%n) (x%n)∗(y%n)的范围
由二维前缀和以及容斥原理可知: a n s = f ( c , d ) − f ( c , b − 1 ) − f ( a − 1 , d ) + f ( a − 1 , b − 1 ) ans=f(c,d)−f(c,b−1)−f(a−1,d)+f(a−1,b−1) ans=f(c,d)−f(c,b−1)−f(a−1,d)+f(a−1,b−1)
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e3 + 10;
char mp1[N][N];
int sum[N][N];
int n, q;
LL cal(int x, int y) {
LL res = 0;
res += 1ll * (x / n) * (y / n) * sum[n][n];
res += 1ll * (y / n) * sum[x % n][n];
res += 1ll * (x / n) * sum[n][y % n];
res += 1ll * sum[x % n][y % n];
return res;
}
int main() {
cin >> n >> q;
for (int i = 0; i < n; i++)
for (int j = 0; j < n; j++)
cin >> mp1[i][j];
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
sum[i][j] = (mp1[i - 1][j - 1] == 'B') + sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1];
}
}
while (q--) {
int a, b, c, d;
cin >> a >> b >> c >> d;
cout << cal(c + 1, d + 1) - cal(c + 1, b) - cal(a, d + 1) + cal(a, b) << endl;
}
return 0;
}
E Set Meal(数据结构)
题意:
n n n道主菜, m m m道副菜,现在要选择一种主菜一种副菜作为一种组合菜。有 L L L种主菜+副菜的组合菜是不提供的。询问剩下的组合菜价值之和最高的是多少。
分析:
将 a a a, b b b数组从小到大排序之后,把它们的组合看成一个 n ∗ m n*m n∗m 的矩阵,那么我们每次只需要取右下角这个最大的值出来,先判断可不可以选,如果可以选, 那它就是答案,否则继续将这个值左边的值和上边的值放进大根堆里继续重复即可。注意判断每个数有没有被拿过,拿过的数不要重复拿。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define PII pair<int, int>
#define x first
#define y second
int main() {
int n, m, l;
cin >> n >> m >> l;
vector<PII> a(n), b(m);
for (int i = 0; i < n; i++) {
cin >> a[i].x;
a[i].y = i;
}
for (int i = 0; i < m; i++) {
cin >> b[i].x;
b[i].y = i;
}
sort(a.begin(), a.end());
sort(b.begin(), b.end());
set<PII> ban;
while (l--) {
int c, d;
cin >> c >> d;
ban.insert({c - 1, d - 1});
}
priority_queue<tuple<int, int, int>> q;
set<PII> vis;
q.push({a[n - 1].x + b[m - 1].x, n - 1, m - 1});
vis.insert({n - 1, m - 1});
while (!q.empty()) {
auto [v, i, j] = q.top();
q.pop();
if (!ban.count({a[i].y, b[j].y})) {
cout << v << '\n';
return 0;
}
if (i - 1 >= 0 && !vis.count({i - 1, j})) {
q.push({a[i - 1].x + b[j].x, i - 1, j});
vis.insert({i - 1, j});
}
if (j - 1 >= 0 && !vis.count({i, j - 1})) {
q.push({a[i].x + b[j - 1].x, i, j - 1});
vis.insert({i, j - 1});
}
}
return 0;
}
F Palindrome Query(数据结构+哈希)
题意:
给出一个长度为 n n n的字符串 s s s,第一种操作:修改 s [ x ] = c s[x]=c s[x]=c,第二种操作:判断 s [ l , r ] s[l,r] s[l,r]是不是回文串。
分析:
用树状数组维护字符串的正序哈希值和逆序哈希值,修改操作相当于在树状数组中先求出删去一个字母的哈希值,再计算加上这个字母的哈希值。判断回文操作只要求出 [ l , r ] [l,r] [l,r]的正序哈希值和逆序哈希值,判断是否相等即可。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
#define lowbit(x) (x & (-x))
const int modd = 1e9 + 7;
LL n, q, pw[1000005], t1[1000005], t2[1000005];
string s;
LL binpow(LL a, LL b, LL m) {
a %= m;
LL res = 1;
while (b > 0) {
if (b & 1)
res = res * a % m;
a = a * a % m;
b >>= 1;
}
return res;
}
void ins(LL x, char e) { //插入
for (int i = x; i <= n; i += lowbit(i))
t1[i] = (t1[i] + (e - 'a') * pw[x - 1] % modd) % modd;
for (int i = x; i <= n; i += lowbit(i))
t2[i] = (t2[i] + (e - 'a') * pw[n - x] % modd) % modd;
}
void upd(LL x, char e) {//更新
for (int i = x; i <= n; i += lowbit(i))
t1[i] = (t1[i] - (s[x] - 'a') * pw[x - 1] % modd + modd) % modd;
for (int i = x; i <= n; i += lowbit(i))
t2[i] = (t2[i] - (s[x] - 'a') * pw[n - x] % modd + modd) % modd;
for (int i = x; i <= n; i += lowbit(i))
t1[i] = (t1[i] + (e - 'a') * pw[x - 1] % modd) % modd;
for (int i = x; i <= n; i += lowbit(i))
t2[i] = (t2[i] + (e - 'a') * pw[n - x] % modd) % modd;
s[x] = e;
}
LL qry(LL l, LL r) {//查询
LL res1 = 0, res2 = 0, L = (l + r + 1) / 2, R = r;
for (int i = R; i >= 1; i -= lowbit(i))
res1 = (res1 + t1[i]) % modd;
for (int i = L - 1; i >= 1; i -= lowbit(i))
res1 = (res1 - t1[i] + modd) % modd;
res1 = res1 * binpow(pw[L - 1], modd - 2, modd) % modd;
L = l, R = (l + r) / 2;
for (int i = R; i >= 1; i -= lowbit(i))
res2 = (res2 + t2[i]) % modd;
for (int i = L - 1; i >= 1; i -= lowbit(i))
res2 = (res2 - t2[i] + modd) % modd;
res2 = res2 * binpow(pw[n - R], modd - 2, modd) % modd;
return res1 == res2;
}
void solve() {
cin >> n >> q;
cin >> s;
s = ' ' + s;
pw[0] = 1;
for (int i = 1; i <= n; ++i)
pw[i] = pw[i - 1] * 1331 % modd;
for (int i = 1; i <= n; ++i)
ins(i, s[i]);
while (q--) {
int op;
cin >> op;
if (op == 1) {
LL x;
cin >> x;
char e;
cin >> e;
upd(x, e);
} else {
int l, r;
cin >> l >> r;
if (qry(l, r))
cout << "Yes" << endl;
else
cout << "No" << endl;
}
}
}
int main() {
int t;
t = 1;
while (t--) {
solve();
}
return 0;
}
学习交流
以下为学习交流QQ群,群号: 546235402,每周题解完成后都会转发到群中,大家可以加群一起交流做题思路,分享做题技巧,欢迎大家的加入。