2024-05-14-ABC353
ABC353讲解
A - Buildings
题意:给定n个数字,输出第一个大于第一个数的下标。
#include<bits/stdc++.h>
#define rep(i,n) for(int i=0;i<(int)n;i++)
using namespace std;
int main()
{
int n,a[105];
cin>>n;
rep(i,n) cin>>a[i];
rep(i,n)
{
if(!i) continue;
if(a[i]>a[0])
{
cout<<i+1;
return 0;
}
}
cout<<-1;
return 0;
}
B - AtCoder Amusement Park
题意:n组,每组若干个人,坐云霄飞车。每个飞车只有k个座位。依次给这n组人安排飞车,若该组人可以坐进飞车,则坐。否则另开一个新的飞车给他们坐。
问最后用了多少个飞车。
#include <bits/stdc++.h>
using namespace std;
int main() {
int n, k;
cin >> n >> k;
int ans = 0;
int cur = k;
for (int i = 0; i < n; i++) {
int x;
cin >> x;
if (cur < x) {
++ans;
cur = k;
}
cur -= x;
}
cout << ans + 1 << endl;
return 0;
}
C - Sigma Problem
题意:求\(∑^{n−1}_{i=1}∑^n_{j=i+1}(a_i+a_j) mod 10^8\)
解法:注意题意是先模再求和。根据数据范围可以看出,\(a_i+a_j\)一定小于\(2 \times 10^8\),即\((a_i+a_j) mod 10^8\)等于\(a_i+a_j\)或\(a_i+a_j-10^8\)。
那么我们先将数组排序,在遍历每一个数时,二分出其和大于\(10^8\)的分界线,两段利用前缀和分别计算答案即可。
#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;
int mo = 1e8;
vector<LL> a(n);
for (auto& x : a)
cin >> x;
sort(a.begin(), a.end());
vector<LL> presum(n);
partial_sum(a.begin(), a.end(), presum.begin());
LL ans = 0;
LL sum = 0;
for (int i = 0; i < n; ++i) {
LL bu = mo - a[i];
if (a[i] < bu) {
ans += a[i] * i + sum;
} else {
auto pos = lower_bound(a.begin(), a.begin() + i, bu) - a.begin();
LL p = pos > 0 ? presum[pos - 1] : 0;
ans += a[i] * pos + p;
LL suf = sum - p;
ans += suf - bu * (i - pos);
}
sum += a[i];
}
cout << ans << '\n';
return 0;
}
D - Another Sigma Problem
题意:求\(∑^{n−1}_{i=1}∑^n_{j=i+1}f(a_i,a_j) mod 998244353\),其中\(f(a,b)\)为把\(a,b\)两数拼接起来。
解法:这题与上题不同在是求总和的模,那么我们直接考虑枚举到\(a_i\)的\(∑^n_{j=i+1}f(a_i,a_j)\)。
当\(a_j\)每个数作为低位的时候,他们会直接加上自己的数值,即\(i\)的后缀和。
当\(a_i\)作为高位的时候,每次会先乘上\(10^{低位位数}\)再加到答案里,那么总和来看就是\(10^{低位位数}\)的后缀和。
所以我们从后向前枚举,维护两个后缀和即可。
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
const int mo = 998244353;
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 sum = 0;
LL ten = 0;
auto calc_ten = [&](int x) {
int cnt = 0;
while (x) {
++cnt;
x /= 10;
}
LL val = 1;
while (cnt--)
val *= 10;
return val;
};
for (int i = n - 1; i >= 0; --i) {
sum += 1ll * a[i] * i % mo;
sum += 1ll * a[i] * ten % mo;
sum %= mo;
LL val = calc_ten(a[i]);
ten += val;
ten %= mo;
}
cout << sum << '\n';
return 0;
}
E - Yet Another Sigma Problem
题意:求\(∑^{n−1}_{i=1}∑^n_{j=i+1}lcp(s_i,s_j)\),lcp意为最长公共前缀。
解法:将所有字符串压进字典树,每个节点上存有多少个字符串,然后对每个个数大于2的点求和即可。
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
const int SZ = 26;
template <typename T, typename K> struct Trie {
struct node {
K value;
bool is_terminal;
int vis_count;
array<int, SZ> children;
node(K val) : value(val) {
is_terminal = false;
children.fill(0);
vis_count = 0;
}
};
int cast(K val) {
int ret = val - 'a';
assert(ret < SZ and ret >= 0);
return ret;
}
vector<node> tree;
Trie(K val) { tree.push_back(node(val)); }
void insert(const T& sequence) {
int cur = 0;
for (int i = 0; i < (int)sequence.size(); i++) {
K value = sequence[i];
if (tree[cur].children[cast(value)] == 0) {
tree[cur].children[cast(value)] = (int)tree.size();
tree.emplace_back(value);
}
cur = tree[cur].children[cast(value)];
tree[cur].vis_count += 1;
}
tree[cur].is_terminal = true;
}
LL dfs(int cur) {
LL sum = 0;
for (int i = 0; i < SZ; i++) {
if (tree[cur].children[i] == 0)
continue;
int child_node = tree[cur].children[i];
sum += dfs(child_node);
}
sum += 1ll * tree[cur].vis_count * (tree[cur].vis_count - 1) / 2;
return sum;
}
};
int main(void) {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n;
cin >> n;
Trie<string, char> tree('a');
for (int i = 0; i < n; i++) {
string s;
cin >> s;
tree.insert(s);
}
LL ans = tree.dfs(0);
cout << ans << '\n';
return 0;
}
F - Tile Distance
题意:给定一个形如下图的地图,L是大瓷砖,S是小瓷砖,每一个小瓷砖块是\(K \times K\), 从一块瓷砖走到另一块瓷砖要花1的代价,请问从起点到终点最少要花多少代价。
解法:分类讨论
首先判掉直接曼哈顿距离走过去的 case。
否则可以发现,一定是从 S 径直走到一个大格子,通过若干大格子,最后径直走到 T。
可以发现,我们最少要花费 2 的代价,从一个大格子走到(对角)相邻的大格子。
在考虑其他的移动方式:从一个大格子走到隔着一堆小格子的大格子,需要花费 k+1 的代价。当且仅当k≤2时是较优的。
那么枚举一下S,T 相邻的大格子,然后算一下距离即可,这部分是简单的。
#include<bits/stdc++.h>
#define ll long long
#define PB(x,y) push_back({x,y})
#define MP make_pair
#define fi first
#define se second
using namespace std;
ll k,sx,sy,tx,ty;
vector<pair<pair<ll,ll>,ll> >tg1,tg2;
ll dis(pair<ll,ll>x,pair<ll,ll>y)
{
ll dx=abs(x.fi-y.fi),dy=abs(x.se-y.se);
if(k==2)return 2*min(dx,dy)+3*abs(dx-dy)/2;
return 2*max(dx,dy);
}
int main()
{
ios::sync_with_stdio(false),cin.tie(nullptr);
cin>>k>>sx>>sy>>tx>>ty;
if(k==1)return cout<<abs(sx-tx)+abs(sy-ty),0;
if((sx/k+sy/k)&1)tg1.PB(MP(sx/k,sy/k),0);//起点在大方格中
else
{
tg1.PB(MP(sx/k-1,sy/k),sx%k+1);//起点在小方格中,枚举周围的大方格
tg1.PB(MP(sx/k+1,sy/k),k-sx%k);
tg1.PB(MP(sx/k,sy/k-1),sy%k+1);
tg1.PB(MP(sx/k,sy/k+1),k-sy%k);
}
if((tx/k+ty/k)&1)tg2.PB(MP(tx/k,ty/k),0);
else
{
tg2.PB(MP(tx/k-1,ty/k),tx%k+1);
tg2.PB(MP(tx/k+1,ty/k),k-tx%k);
tg2.PB(MP(tx/k,ty/k-1),ty%k+1);
tg2.PB(MP(tx/k,ty/k+1),k-ty%k);
}
ll ans=abs(sx-tx)+abs(sy-ty);
for(auto i:tg1)
for(auto j:tg2)
ans=min(ans,dis(i.fi,j.fi)+i.se+j.se);
cout<<ans;
return 0;
}