AtCoder Beginner Contest 258 A - F
这场打的有一种我又行了的感觉
A - When?
向下取整 + 取余
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <string>
#include <queue>
#include <functional>
#include <map>
#include <set>
#include <cmath>
#include <cstring>
#include <deque>
#include <stack>
using namespace std;
typedef long long ll;
#define pii pair<int, int>
const ll maxn = 2e5 + 10;
const ll inf = 1e17 + 10;
int main()
{
int x;
scanf("%d", &x);
printf("%02d:%02d\n", 21 + x / 60, x % 60);
return 0;
}
B - Number Box
暴力枚举
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <string>
#include <queue>
#include <functional>
#include <map>
#include <set>
#include <cmath>
#include <cstring>
#include <deque>
#include <stack>
using namespace std;
typedef long long ll;
#define pii pair<int, int>
const ll maxn = 2e5 + 10;
const ll inf = 1e17 + 10;
string s[20];
int n;
const int xi[8] = {-1, -1, -1, 0, 0, 1, 1, 1};
const int yi[8] = {-1, 0, 1, -1, 1, -1, 0, 1};
ll solve(int x, int y, int k)
{
ll ans = s[x][y] - '0';
for(int i=1; i<n; i++)
{
x += xi[k];
y += yi[k];
x = (x + n) % n;
y = (y + n) % n;
ans = ans * 10 + s[x][y] - '0';
}
return ans;
}
int main()
{
cin >> n;
for(int i=0; i<n; i++) cin >> s[i];
ll ans = 0;
for(int i=0; i<n; i++)
{
for(int j=0; j<n; j++)
{
for(int k=0; k<8; k++)
{
ans = max(solve(i, j, k), ans);
}
}
}
cout << ans << endl;
return 0;
}
C - Rotation
将其看做一个循环的字符串,操作一就是当前指针向后移 x 个位置
操作二就是当前指针的后 x 个位置是啥
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <string>
#include <queue>
#include <functional>
#include <map>
#include <set>
#include <cmath>
#include <cstring>
#include <deque>
#include <stack>
using namespace std;
typedef long long ll;
#define pii pair<int, int>
const ll maxn = 2e5 + 10;
const ll inf = 1e17 + 10;
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, q;
cin >> n >> q;
string s;
cin >> s;
int tp = 0;
while(q--)
{
int a, b;
cin >> a >> b;
if(a == 1) tp = (tp - b + n) % n;
else
cout << s[(tp + b - 1 + n) % n] << '\n';
}
return 0;
}
D - Trophy
贪心
打游戏的时候都知道,解锁前置关卡,直到打到收益最高的关卡就一直刷副本
因此答案肯定是解锁完前置关卡就不断刷,找到最小值就行
巨大坑:初始值一定要很大很大(不会有人因此 wa 了 3 发吧)
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <string>
#include <queue>
#include <functional>
#include <map>
#include <set>
#include <cmath>
#include <cstring>
#include <deque>
#include <stack>
using namespace std;
typedef unsigned long long ll;
#define pii pair<int, int>
const ll maxn = 2e5 + 10;
const ll inf = 4e18 + 10;
ll a[maxn], b[maxn];
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
ll n, m;
cin >> n >> m;
for(int i=0; i<n; i++)
cin >> a[i] >> b[i];
ll ans = inf, temp = 0;
for(int i=0; i<n && m; i++)
{
m--;
temp += a[i] + b[i];
ans = min(ans, temp + b[i] * m);
}
cout << ans << endl;
return 0;
}
E - Packing Potatoes
二分 + 循环
无限多的物品,模拟是不现实的,考虑从 \(n\) 种物品下手
在第 \(i\) 种物品中,我们可以计算,装满盒子之后需要连续放多少个物品,而且还能知道装完之后是在哪种物品停下来
因此我们考虑建立一条有向边,\(i -> j\) 代表如果当前盒子第一个放入的是第 \(i\) 种物品,则下一个盒子的第一个放入的物品是第 \(j\) 种物品
这样每种物品都会发出一条有向边,总共是 \(n\) 条有向边
不难发现,从第一个物品开始往后跳转,如果这样一直跳转下去的话,会存在一个循环
我们只要对询问的答案,判断一下是否能进入循环,如果不进入,就直接返回答案;如果进入,就取模判断一下最后在循环的哪一步停下来
这样一个无穷大的问题就变成一个循环取模的问题
建立有向边的时候考虑用 前缀和 + 二分 的方式来求,剩下的都是亿点细节问题
细节:
-
二分的时候,可能会存在 \(n\) 种物品全部取出来都不够装一个盒子的,因此要提前将 \(x\) 对 \(sum\) 取模
-
求取答案未进入到循环之前,不能进入有向边一个一个找,要提前预处理好进入循环前,第 \(i\) 个盒子是哪一步,不然会 TLE
-
如果是从 \(1\) 开始的,注意好下标,特别是取模的时候
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#include <string>
#include <queue>
#include <functional>
#include <map>
#include <set>
#include <cmath>
#include <cstring>
#include <deque>
#include <stack>
using namespace std;
typedef long long ll;
#define pii pair<int, int>
const ll maxn = 2e5 + 10;
const ll inf = 1e16 + 10;
ll num[maxn], sum[maxn], nex_num[maxn];
int nex[maxn], vis[maxn];
int alp[maxn];
int main()
{
int n, q, x;
cin >> n >> q >> x;
for(int i=1; i<=n; i++)
{
cin >> num[i];
sum[i] = sum[i-1] + num[i];
}
ll rx = x / sum[n];
x %= sum[n];
for(int i=1; i<=n; i++)
{
int way = 0;
nex_num[i] = rx * n;
if(sum[n] - sum[i-1] >= x)
{
way = lower_bound(sum + 1, sum + 1 + n, x + sum[i-1]) - sum;
nex_num[i] += way - i + 1;
}
else
{
way = lower_bound(sum + 1, sum + 1 + n, x - sum[n] + sum[i-1]) - sum;
nex_num[i] += n - i + 1 + way;
}
nex[i] = way + 1;
if(nex[i] > n) nex[i] = 1;
}
int now = 1, dep = 1;
while(vis[now] == 0)
{
vis[now] = dep;
alp[dep] = now;
dep++;
now = nex[now];
}
ll pre = vis[now] - 1, rnd = dep - vis[now];
while(q--)
{
ll k;
cin >> k;
ll ans = 0;
if(k <= pre)
ans = nex_num[alp[k]];
else
{
k -= pre + 1;
k %= rnd;
ans = nex_num[alp[k + pre + 1]];
}
cout << ans << endl;
}
return 0;
}
F - Main Street
基本的思路就是,点到点的最短路径只能是两种方式:
-
直接走,不走通道
-
走通道
走通道的做法就考虑必经点,即起点对 4 个方向各发出一条射线,与会最近的通道分别产生 \(4\) 个交点,走通道的最短路必然会经过这四个点的其中一个
同理,终点也能产生四个必经点
走通道的最短路,必然是在这两组点的最短路之间产生,总共 \(16\) 种方式
答案就取最小值就好了
以上均为我的想法,代码实现还没写,有些细节还要处理下
(不鸽,会补题的!)
更新:
感觉细节真的很多,参考了一下 jiangly 和 tourist 的代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <array>
#include <vector>
using namespace std;
typedef long long ll;
#define pii pair<ll, ll>
ll B, K, sx, sy, ex, ey;
pii query(ll x, ll y, int way)
{
if(way == 0) x -= x % B;
else if(way == 1) x += B - x % B;
else if(way == 2) y -= y % B;
else y += B - y % B;
return {x, y};
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while(t--)
{
cin >> B >> K >> sx >> sy >> ex >> ey;
ll ans = (abs(sx - ex) + abs(sy - ey)) * K;
for(int i=0; i<4; i++)
{
for(int j=0; j<4; j++)
{
auto [qx, qy] = query(sx, sy, i);
auto [px, py] = query(ex, ey, j);
ll cnt = (abs(sx - qx) + abs(sy - qy) + abs(ex - px) + abs(ey - py)) * K;
if(qx % B == 0 && px % B == 0 && qy / B == py / B)
{
ll r = qy % B + py % B;
if(qx != px)
cnt += abs(qx - px) + min(r, 2 * B - r);
else
cnt += abs(qy - py);
}
else if(qy % B == 0 && py % B == 0 && qx / B == px / B)
{
ll r = qx % B + px % B;
if(qy != py)
cnt += abs(qy - py) + min(r, 2 * B - r);
else
cnt += abs(qx - px);
}
else
cnt += abs(qx - px) + abs(qy - py);
ans = min(ans, cnt);
}
}
cout << ans << '\n';
}
return 0;
}