AtCoder Beginner Contest 280 A-G
AtCoder Beginner Contest 280 A-G
https://atcoder.jp/contests/abc280
个人认为D >> E,F
被D搞心态了,导致EF都没看()
A - Pawn on a Grid
统计#的个数
#include <bits/stdc++.h>
using namespace std;
int main () {
int n, m, cnt = 0;
cin >> n >> m;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
char ch;
cin >> ch;
if (ch == '#') cnt ++;
}
}
cout << cnt;
}
B - Inverse Prefix Sum
根据前缀和还原原数组
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 15;
int a[N], n;
signed main () {
cin >> n;
for (int i = 1; i <= n; i++) cin >> a[i], cout << a[i] - a[i-1] << ' ';
}
C - Extra Character
s中插入一个字符变成t,求插入位置
注意特判末尾
#include <bits/stdc++.h>
#define int long long
using namespace std;
signed main () {
string s, t;
cin >> s >> t;
for (int i = 0; i < s.size (); i++) {
if (s[i] != t[i]) {
cout << i + 1;
return 0;
}
}
cout << s.size () + 1;
}
D - Factorial and Multiple
先对k进行唯一分解,预处理出p和cnt数组,用来记录素因数及其个数
对于k, 大于\(\sqrt k\) 的因子只有一个,所以1e12的范围只需考虑到1e6即可。
因此如果k的最大素因子大于1e6,就直接取这个因子。
否则可以二分,范围从1到k,check当前mid是否能涵盖所有的因子。
(特别注意下tmp那里)
快速计算 \(1-x\) 中各质因数数量之和的公式:\(tot=\frac{x}{p_i}+\frac{x}{p_i^2}+...+\frac{x}{p_i^k}, (x\geq p_i^k且x<p_i^{k+1})\)
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e6 + 5;
int n, p[N], cnt[N], tt;
void test (int tt, int *p, int *cnt) {
for (int i = 1; i <= tt; i++) cout << p[i] << ' ' << cnt[i] << endl;
cout << endl;
}
void pre () {
for (int i = 2; i * i <= n; i++) {
if (n % i == 0) {
p[++tt] = i;
while (n % i == 0) {
n /= i;
cnt[tt] ++;
}
}
}
if (n > 1) {
p[++tt] = n;
cnt[tt] ++;
}
}
bool check (int x) {
//cout << x << ": ";
for (int i = 1; i <= tt; i++) {
int tot = 0, pr = p[i], tmp = x;
while (tmp) {
tot += (tmp / pr);
tmp /= pr;
//cout << tmp << ' ';
}
//cout << "tot: " << tot << ' ';
if (tot < cnt[i]) return false;
}
return true;
}
signed main () {
cin >> n;
int m = n;
pre ();
//test (tt, p, cnt);
if (p[tt] > 1e6) {
cout << n;
return 0;
}
int l = 1, r = m;
while (l < r) {
int mid = l + r >> 1;
if (check (mid)) r = mid;
else l = mid + 1;
//cout << "end: " << l << ' ' << r << endl;
}
cout << r << endl;
}
//预处理素数表
//二分n
//chceck x!是否能覆盖所有分解
E - Critical Hit
题意:现在是0,有 \(\frac{p}{100}\) 的概率打出2的攻击,\(1-\frac{p}{100}\) 的概率打出1的攻击。求打出n的攻击的期望值,模998244353
分析:就是裸的期望dp,式子:\(f_i = (f_{i-2} * \frac{p}{100} + f_{i-1} * (1-\frac{p}{100})) + 1\),记得取模,分数求逆元。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e5 + 5, mod = 998244353;
int n, p, f[N];
int qmi(int a,int k, int p) {
int ans = 1;
while (k) {
if (k & 1)//末位1取出
ans = ans * a % p;
k >>= 1;//次末位
a = a * a % p;
}
return ans;
}
signed main () {
cin >> n >> p;
f[1] = 1;
int p1 = p * qmi (100, mod - 2, mod) % mod, p2 = (1 - p1 + mod) % mod;
//cout << p1 << ' ' << p2 << endl;
for (int i = 2; i <= n; i++) {
f[i] = (f[i-1] * p2 % mod + f[i-2] * p1 % mod + 1) % mod;
}
cout << f[n];
}
F - Pay or Receive
题意:给定一个n个点的有向图,输入m条边,边 \({a,b,c}\) 对应一条 \(a\) 到 \(b\) 的权为 \(c\) 的边以及一条 \(b\) 到 \(a\) 的权为 \(-c\) 的边。然后q次询问,问从a点走到b点的最大距离。若不可达输出nan,若距离无穷输出inf,其余情况输出最大值。
分析:把图建起来之后,先判可达性。用并查集判两点是否在一个集合中。对于inf的情况,模拟样例可知,若两点之间可以经过正环,则可以通过在上面一直绕圈圈的方式达到无穷。所以对于每点出发,跑bfs,若发现到达同一点距离却不同的情况就是出现了正环。若无正环且可达,输出 \(dis_b-dis_a\)。
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1e5 + 5, M = N * 2, inf = -1e18;
int n, m, q;
int h[N], e[M], ne[M], w[M], idx;
int fa[N], cnt[N], dis[N];
bool vis[N], isINF[N];
void add (int a, int b, int c) {
e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx ++;
}
int find (int x) {
if (x != fa[x]) fa[x] = find (fa[x]);
return fa[x];
}
void Merge (int a, int b) {
a = find (a), b = find (b);
if (a != b) fa[a] = b;
}
void bfs (int st) {
queue <int> q;
q.push (st);
vis[st] = true;
dis[st] = 0;
while (!q.empty ()) {
auto t = q.front ();
q.pop ();
for (int i = h[t]; ~i; i = ne[i]) {
int j = e[i];
if (dis[j] == inf) {
dis[j] = dis[t] + w[i];
vis[j] = true;
q.push (j);
}
else if (dis[j] != dis[t] + w[i]) {
isINF[find (st)] = true;
return ;
}
}
}
}
signed main () {
memset (h, -1, sizeof h);
cin >> n >> m >> q;
for (int i = 1; i <= n; i++) fa[i] = i, dis[i] = inf;
for (int i = 0; i < m; i++) {
int a, b, c;
cin >> a >> b >> c;
add (a, b, c), add (b, a, -c);
if (find (a) != find (b)) Merge (a, b);
}
for (int i = 1; i <= n; i++) {
int j = find (i);
if (vis[j]) continue;
vis[j] = true;
bfs (j);
}
while (q --) {
int a, b;
cin >> a >> b;
if (find (a) != find (b)) {
cout << "nan\n";
continue;
}
if (isINF[find (a)]) cout << "inf\n";
else cout << dis[b] - dis[a] << endl;
}
}
//找正环
G - Do Use Hexagon Grid 2
题意:
如图所示的六边形,相邻六边形之间的距离为1,给定点集S,求S的非空子集的个数,使得子集内任意两点的最短距离不超过 \(d\)。
分析:
先分成四象限计算各点到(0,0)的距离
归纳得出点 \((x,y)\) 到 \((0,0)\) 的距离为 \(max(|x|,|y|,|x-y|)\)。
可以把二维点坐标 \((x,y)\) 扩展到三维:\((x,y,x-y)\)。
此时选择满足条件的点集就相当于这若干个点能被一个 \(d*d*d\) 的正方体所包含。
先给点排序,尽量聚拢一起。
根据三个维度设dp式子。\(f[x][y][z]\) 表示当前第一个维度枚举到第 \(x\) 个点,第二个维度枚举到第 \(y\) 个点,第三个维度枚举到第 \(z\) 个点的合法方案数。转移的时候记得更新最远距离点。
#include <bits/stdc++.h>
#define int long long
using namespace std;
typedef pair<int, int> pii;
const int N = 305, mod = 998244353;
int n, D, f[N][N][N], ans; //f[x][y][z]: x,y,z的长方体
pii a[N];
signed main() {
cin >> n >> D;
for (int i = 1; i <= n; i++) cin >> a[i].first >> a[i].second;
sort(a + 1, a + n + 1);
for (int i = 1; i <= n; i++) { //选到第i个点
f[i][i][i] = 1; //单个点算一个方案
ans ++; //(a[i].first, a[i].second, a[i].first - a[i].second);
for (int x = 1; x < i; x++) { //x: 1~i
if (a[i].first - a[x].first > D) continue;
for (int y = x; y < i; y++) { //y: x~i (保证不和a[x]重)
if (abs(a[i].second - a[y].second) > D) continue;
for (int z = x; z < i; z++) { //z: x~i
if (!f[x][y][z] || (a[i].first - a[z].first) + (a[z].second - a[i].second) > D) continue;
int yy = y, zz = z; //更新max距离值
if (a[i].second < a[y].second) yy = i;
if (a[i].first - a[i].second < a[z].first - a[z].second) zz = i;
ans = (ans + f[x][y][z] + mod) % mod;
f[x][yy][zz] = (f[x][yy][zz] + f[x][y][z] + mod) % mod;
}
}
}
}
cout << ans;
}
Ex - Substring Sort
蹲蹲大佬的题姐。