2024-11-5-ABC378
ABC378
A - Pairing
题意
给定4个球。
问可以重复多少次以下操作:每次选两个相同颜色的球,然后丢弃。
思路
数一下每种颜色的球有几个,然后除2加一下。
#include <bits/stdc++.h>
using namespace std;
int cnt[4];
int main() {
for (int i = 0; i < 4; i++) {
int A;
cin >> A;
cnt[A - 1]++;
}
int ans = 0;
for (int i = 0; i < 4; i++) {
ans += cnt[i] / 2;
}
cout << ans << "\n";
return 0;
}
B - Garbage Collection
题意
n种垃圾,第i种垃圾会在天数d收取,其中d满足\(d\%q_i=r_i\)。(就是隔qi加一个ri)
回答q个询问,每个询问问在第di天丢的第ti种垃圾,会在第几天被收取。如果当天丢且当天可收取,则会被收取。
思路
先算r=d % q[t]
,如果r<=r[t]
,那么再过r[t]-r
天就好了,不然就要再等一个q[t]
的时间。
#include <bits/stdc++.h>
using namespace std;
int q[105],r[105];
int main() {
int n;
cin >> n;
for(int i=0;i<n;i++)
cin>>q[i]>>r[i];
int Q;
cin >> Q;
while (Q--) {
int t, d;
cin >> t >> d;
--t;
int ans = (r[t] - d % q[t] + q[t]) % q[t];
cout << d + ans << '\n';
}
return 0;
}
C - Repeating
题意
给定一个数组\(a\),构造相同长度的数组\(b\),满足\(b_i\)是\(a_i\)上一次出现的位置,或者−1。
思路
用map记录每个\(a_i\)上次出现的位置,然后顺序输出map[a[i]]
即可
#include <bits/stdc++.h>
using namespace std;
int main() {
int n;
cin >> n;
map<int, int> pos;
for (int i = 0; i < n; i++) {
int x;
cin >> x;
if(pos.count(x)) cout<<pos[x]+1<<" ";
else cout<<"-1 ";
pos[x] = i;
}
return 0;
}
D - Count Simple Paths
题意
给定一张二维平面,有障碍物。
问方案数,从任意点出发,上下左右走,可以走kk步,不经过障碍物,且每个点只访问一次。
思路
暴力
#include<bits/stdc++.h>
using namespace std;
int dx[] = {0, 1, -1, 0};
int dy[] = {1, 0, 0, -1};
int h, w, k;
string s[15];
int ans;
bool vis[15][15];
bool check(int x, int y) {
return (x >= 0 && y >= 0 && x < h && y < w && !vis[x][y] && s[x][y] == '.');
}
void dfs(int x, int y, int d) {
if (d == k) {
ans++;
return;
}
vis[x][y] = 1;
for (int i = 0; i < 4; i++) {
int xx = x + dx[i], yy = y + dy[i];
if (check(xx, yy)) {
dfs(xx, yy, d + 1);
}
}
vis[x][y] = 0;
}
int main() {
cin >> h >> w >> k;
for (int i = 0; i < h; i++)
cin >> s[i];
for (int i = 0; i < h; i++) {
for (int j = 0; j < w; j++) {
if (check(i, j))
dfs(i, j, 0);
}
}
cout << ans;
return 0;
}
E - Mod Sigma Problem
题意
给定一个数组,求所有连续区间和modM后的总和(最后答案不取模)
解法
求区间总和的经典方法就是前缀和,设\(S_i=(A_1+A_2+...+A_i)mod M\),就是前i项的总和,那么最后的答案就是求对于所有的连续区间\(l,r\)的\((S_r-S_{l-1})modM\)的总和。
由于\(S_i\)都是小于M的,所以式子还可以改写一下。
现在我们考虑固定r,然后计算出r不变l变的和,那我们要设\(X_r\)为\(S_{l-1}>S_r\)的数量,这样我们就可以知道在r不变时加了多少个M,也就可以列出式子:
不难发现 \(∑^r_{l=1}S_{l-1}\)是S数组的前缀和,也就是说只要能快速求出\(X_r\)问题就解决了。
预处理出前缀和数组后,问题就是查询当前数前面有多少个数大于当前数,这个问题可以用树状数组搞定。
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn = 200005;
struct FenwickTree {
vector<ll> tree;
int n;
FenwickTree(int size) {
n = size;
tree.resize(size + 1, 0);
}
void update(int index, int delta) {
for (++index; index <= n; index += index & -index) {
tree[index] += delta;
}
}
int query(int index) {
int sum = 0;
for (++index; index > 0; index -= index & -index) {
sum += tree[index];
}
return sum;
}
};
vector<ll> countGreaterThan(const vector<ll>& nums) {
int n = nums.size();
// 建立一个副本以进行排序和离散化
vector<ll> sorted_nums = nums;
sort(sorted_nums.begin(), sorted_nums.end());
vector<ll> result(n);
FenwickTree fenwickTree(n);
// 从后到前遍历
for (int i = 0; i < n; i++) {
// 计算当前数的离散化索引
int index = lower_bound(sorted_nums.begin(), sorted_nums.end(), nums[i]) - sorted_nums.begin();
// 查询前面有多少个数是小于等于当前数
result[i] = fenwickTree.query(n - 1) - fenwickTree.query(index);
// 更新树状数组,增加当前数的出现次数
fenwickTree.update(index, 1);
}
return result;
}
int n;
ll m, a[maxn];
vector<ll> s;
int main() {
cin >> n >> m;
s.resize(n + 1);
for (int i = 1; i <= n; i++) {
cin >> a[i];
s[i] = (s[i - 1] + a[i]) % m;
}
vector<ll> x = countGreaterThan(s);
ll ans = 0, sum = 0;
for (int i = 1; i <= n; i++) {
ans += s[i] * i - sum + m * x[i];
sum += s[i];
}
cout << ans;
return 0;
}