AtCoder Beginner Contest 266 A-G
AtCoder Beginner Contest 266 A-G
https://atcoder.jp/contests/abc266
A - Middle Letter
输出字符串最中间的那个字母
#include <bits/stdc++.h>
using namespace std;
int main () {
string s;
cin >> s;
cout << s[(s.size()+1)/2-1];
}
B - Modulo Number
题意:\(N\in [-10^{18}, 10^{18}], x\in [0, 998244352]\), 给定 \(N\), 求 \(x\),使得 \((N-x) \%\,998244353=0\)
分析:不难发现就是要找到一个和 \(N\) 同余的 \(x\)
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int mod = 998244353;
int n;
int MOD (int x) {
while (x < 0) x += mod;
return x % mod;
}
signed main () {
cin >> n;
cout << MOD (n);
}
//倍数
//N与x同余
C - Convex Quadrilateral
题意:给四个点的坐标,问构成的图形是否为凸多边形
注:给的点满足不重合,不共线,不相邻的边一定没有公共点,也就是保证四个点一定构成四边形
分析:高中知识,分别判断两条对角线是否都能把两个点隔开(不在同一边),判断的方式就是————
设对角线 \(AC\) 方程为 \(Ax+By+C=0\), 若 \((Ax_b+By_b+C)*(Ax_d+By_d+C)<0\), 则 \(B,D\) 在对角线 \(AC\) 的两边;同理再判断一下对角线 \(BD\) 能否把 \(A,C\) 划分到两边即可。
至于直线方程怎么写,相信也不难,两点式一般式之类的都能解出来,我手算的结果是:
\(A = y_1-y_2, B = x_2-x_1, C = x_1*y_2 - x_2*y_1;\)
#include<bits/stdc++.h>
#define int long long
using namespace std;
int x[5], y[5];
signed main () {
for (int i = 1; i <= 4; i++) cin >> x[i] >> y[i];
//判两条对角线
int A, B, C, X, Y;
A = y[1] - y[3], B = x[3] - x[1], C = x[1]*y[3] - x[3]*y[1];
X = A*x[2] + B*y[2] + C, Y = A*x[4] + B*y[4] + C;
//cout << X << ' ' << Y << endl;
if (X * Y > 0) {
cout << "No\n";
return 0;
}
A = y[2] - y[4], B = x[4] - x[2], C = x[2]*y[4] - x[4]*y[2];
X = A*x[1] + B*y[1] + C, Y = A*x[3] + B*y[3] + C;
if (X * Y > 0) {
cout << "No\n";
return 0;
}
cout << "Yes";
}
//判断是否为凸多边形
//判断点是否在直线的两边
//Ax+By+C < 0
//A = y1-y2, B = x2-x1, C = x1y2 - x2y1;
D - Snuke Panic (1D)
这一道题和 \(kuangbin\) 专题里的免费馅饼十分相似
题意:\(t_i\) 时刻在 \(x_i\) 处会出现价值为 \(a_i\) 的东西。人一开始在 \(0\) 的位置,每次只能向右或向左移动一格,且花费一单位时间。人与物品的坐标取值只可能为 \(0,1,2,3,4\)。问人能得到的最大价值为多少
分析:定义 \(dp\) 数组\(f_{i,j}\)表示第 \(i\) 秒时在位置 \(j\) 时所能得到的最大价值,然后逆推转移即可。
#include <bits/stdc++.h>
#define endl "\n"
#define int long long
using namespace std;
typedef pair<int, int> pii;
const int N = 1e5 + 5;
int n, m;
int f[N][7]; //第i秒时在位置j
int dx[] = {-1, 0, 1};
signed main () {
ios::sync_with_stdio (0);cin.tie(0);cout.tie(0);
memset (f, 0, sizeof f);
cin >> n;
for (int i = 0; i < n; i++) {
int ti, xi, ai;
cin >> ti >> xi >> ai;
f[ti][xi] = ai;
m = max (m, ti);
}
//cout << "m=" << m << endl;
for (int t = m-1; t >= 0; t--) {
f[t][0] += max (f[t+1][0], f[t+1][1]);
for (int x = 1; x < 4; x++) {
f[t][x] += max (f[t+1][x], max (f[t+1][x+1], f[t+1][x-1]));
}
f[t][4] += max (f[t+1][4], f[t+1][3]);
cout << endl;
}
cout << f[0][0] << endl;
}
E - Throwing the Die
题意:\(n\) 轮游戏,每次可以丢一枚骰子,每次丢完可以选择结束游戏或者继续。
结束游戏时的分数为最终得分,如果一直按照最优策略来投,求 \(n\) 轮游戏的期望得分。
分析:最开始没读懂题,不知道什么时候结束为最优。
其实就是:如果下一轮投的点数比当前的期望更高,那么继续投下去就可以增加期望,所以会选择继续投
然后就可以直接 \(dp\) 了
#include <bits/stdc++.h>
using namespace std;
const int N = 105;
double f[N];
int main () {
int n;
cin >> n;
f[1] = 3.5;
for (int i = 2; i <= n; i++) {
for (double j = 1.0; j <= 6.0; j++) {
if (j >= f[i-1]) f[i] += j;
else f[i] += f[i-1];
}
f[i] /= 6;
}
cout << fixed << setprecision (8) << f[n] << endl;
}
//f[i]表示第i轮的期望,点数比期望大就继续投
F - Well-defined Path Queries on a Namori
题意
给定一个有 \(n\) 个点,\(n\) 条边的无向图,\(q\) 次查询两点 \(u_i,v_i\) 之间是否只存在一条简单路径(每条边只走一次)
分析
观察样例:
\(n\) 个点 \(n\) 条边,不难得出图上必定有且仅有一个环,那么这就是一个基环树。
相当于是一个环把树劈开,然后环上每一点都连着一条链。
可以以环上的每一点作为根节点,它连着的那条树上的所有点的根都是这个在环上的点。
则只需判断待查询的 \(u,v\) 点的根是否相同。
相同则 \(Yes\),因为不会进入环,有且仅有一条简单路径;
不同则 \(No\), 因为会进入环,不止一条简单路径。
Code
\(topsort+dfs\)
#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5, M = N*2;
int n, q;
int h[N], e[M], ne[M], idx;
int fa[N], d[N];
void add (int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}
void topsort () {
queue <int> q;
for (int i = 1; i <= n; i++) {
if (d[i] == 1) q.push (i);
}
while (!q.empty()) {
int t = q.front();
q.pop ();
fa[t] = -1; //无环
for (int i = h[t]; ~i; i = ne[i]) {
int j = e[i];
if (--d[j] == 1) q.push (j);
}
}
}
void dfs (int u, int v) {
fa[u] = v;
for (int i = h[u]; ~i; i = ne[i]) {
int j = e[i];
if (fa[j] == -1) dfs (j, v);
}
}
int main () {
memset (h, -1, sizeof h);
cin >> n;
for (int i = 0; i < n; i++) {
int a, b;
cin >> a >> b;
add (a, b), add (b, a);
d[a] ++, d[b] ++;
}
//topsort找环
topsort ();
for (int i = 1; i <= n; i++) {
if (fa[i] == 0) dfs (i, i);
}
cin >> q;
while (q --) {
int u, v;
cin >> u >> v;
if (fa[u] == fa[v]) cout << "Yes\n";
else cout << "No\n";
}
}
//dfs合并到环上
G - Yet Another RGB Sequence
题意
输入 \(R,G,B,K\),要求构造字符串(只由 \(R,G,B\) 构成),满足:\(R\) 出现 \(R\) 次,\(G\) 出现 \(G\) 次,\(B\) 出现 \(B\) 次,\(RG\) 出现 \(K\) 次。问可以构造出多少种这样的串。
分析
首先考虑 \(G,B\) 的分配(不会影响到 \(RG\)),方案数为 \(C_{G+B}^G\);
然后在排好的 \(GB\) 串中选 \(K\) 个 \(G\),插入 \(R\), 变成 \(RG\),方案数为 \(C_G^K\);
最后还剩 \(R-K\) 个 \(R\),放进去不能产生新的 \(RG\),所以只能填在 \(B\) 前或 \(R\) 前,方案数为 \(C_{R-K+B+K}^{R-K}=C_{R+B}^{R-K}\)
故答案为 \(C_{G+B}^G *C_G^K *C_{R+B}^{R-K}\)
Code
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
const int P = 998244353, N = 3e6 + 5;
int R, G, B, K;
using i64 = long long;
// assume -P <= x < 2P
int norm(int x) {
if (x < 0) {
x += P;
}
if (x >= P) {
x -= P;
}
return x;
}
template<class T>
T power(T a, i64 b) {
T res = 1;
for (; b; b /= 2, a *= a) {
if (b % 2) {
res *= a;
}
}
return res;
}
struct Z {
int x;
Z(int x = 0) : x(norm(x)) {}
Z(i64 x) : x(norm(x % P)) {}
int val() const {
return x;
}
Z operator-() const {
return Z(norm(P - x));
}
Z inv() const {
assert(x != 0);
return power(*this, P - 2);
}
Z &operator*=(const Z &rhs) {
x = i64(x) * rhs.x % P;
return *this;
}
Z &operator+=(const Z &rhs) {
x = norm(x + rhs.x);
return *this;
}
Z &operator-=(const Z &rhs) {
x = norm(x - rhs.x);
return *this;
}
Z &operator/=(const Z &rhs) {
return *this *= rhs.inv();
}
friend Z operator*(const Z &lhs, const Z &rhs) {
Z res = lhs;
res *= rhs;
return res;
}
friend Z operator+(const Z &lhs, const Z &rhs) {
Z res = lhs;
res += rhs;
return res;
}
friend Z operator-(const Z &lhs, const Z &rhs) {
Z res = lhs;
res -= rhs;
return res;
}
friend Z operator/(const Z &lhs, const Z &rhs) {
Z res = lhs;
res /= rhs;
return res;
}
friend std::istream &operator>>(std::istream &is, Z &a) {
i64 v;
is >> v;
a = Z(v);
return is;
}
friend std::ostream &operator<<(std::ostream &os, const Z &a) {
return os << a.val();
}
};
Z fac[N], inv[N];
Z C(int x,int y){
return fac[x] * inv[y] * inv[x-y];
}
signed main () {
cin >> R >> G >> B >> K;
int N = R + G + B;
fac[0] = 1;
for (int i = 1; i <= N; i++) fac[i] = fac[i-1] * i;
inv[N] = fac[N].inv();
for (int i = N; i; i--) inv[i-1] = inv[i] * i;
Z ans = C(G+B, G) * C(G, K) * C(B+R, R-K);
cout << ans;
}
Ex - Snuke Panic (2D)
题意
同 \(D\), 不同的是二维坐标,初始在 \((0,0)\), 且坐标范围变为 \(0\leq X_i,Y_i\leq 10^9\),规则变为沿 \(x\) 轴方向一次走一格,沿 \(y\)方向一次走一格,\(x\) 轴方向正负方向都能走, \(y\) 轴方向只能沿 正半轴方向走
二位偏序,学了再写
小菜鸟上了个色