A.Thickest Burger
签到题,输出(max(a+2b,2a+b))。
void solve() {
int a, b;
cin >> a >> b;
cout << max(a + 2 * b, 2 * a + b) << '
';
}
int main(int argc, char *argv[]) {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout << fixed << setprecision(10);
int o_o;
for (cin >> o_o; o_o; --o_o) solve();
return 0;
}
B.Relative atomic mass
签到题,计算分子量。
void solve() {
string s;
cin >> s;
int ans = 0;
for (char &ch : s) {
if (ch == 'C') {
ans += 12;
} else if (ch == 'H') {
++ans;
} else {
ans += 16;
}
}
cout << ans << '
';
}
int main(int argc, char *argv[]) {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout << fixed << setprecision(10);
int o_o;
for (cin >> o_o; o_o; --o_o) solve();
return 0;
}
C.Recursive sequence
矩阵快速幂经典题
$ f_n = f_{n - 1} + 2 f_{n - 2} + n^4 $
const int MAXN = 7;
const ull MOD = 2147493647;
typedef ull Mat[MAXN][MAXN];
void mul(Mat &a, const Mat &b) {
static Mat c;
ZERO(c);
FOR2(i, 0, MAXN, j, 0, MAXN)
FOR(k, 0, MAXN) c[i][j] = (c[i][j] + a[i][k] * b[k][j]) % MOD;
memcpy(a, c, sizeof(c));
}
Mat A, B;
const Mat P = {{1, 2, 1, 4, 6, 4, 1}, {1, 0, 0, 0, 0, 0, 0},
{0, 0, 1, 4, 6, 4, 1}, {0, 0, 0, 1, 3, 3, 1},
{0, 0, 0, 0, 1, 2, 1}, {0, 0, 0, 0, 0, 1, 1},
{0, 0, 0, 0, 0, 0, 1}};
void solve() {
int n, a, b;
cin >> n >> a >> b;
if (n < 3) {
cout << (n == 1 ? a : b) << '
';
return;
}
ZERO(A);
MEMCPY(B, P, sizeof(P));
FOR(i, 0, MAXN) A[i][i] = 1;
for (n -= 2; n; n >>= 1, mul(B, B))
if (n & 1) mul(A, B);
ull ans = 0;
const ull L[] = {1ULL * b, 1ULL * a, 16, 8, 4, 2, 1};
FOR(i, 0, MAXN) ans = (ans + A[0][i] * L[i]) % MOD;
cout << ans << '
';
}
int main(int argc, char *argv[]) {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout << fixed << setprecision(10);
int o_o;
for (cin >> o_o; o_o; --o_o) solve();
return 0;
}
E.Counting Cliques
题意:计算一个图中大小为S的完全子图的个数,每个点的度不超过20。
因为每个点的度很小,所以可以暴力dfs。
const int MAXN = 100 + 3;
vi adj[MAXN];
bool connected[MAXN][MAXN], vis[MAXN];
int n, m, s, top, stk[MAXN], ans;
inline bool check(int u) {
FOR(i, 0, top)
if (!connected[stk[i]][u]) return false;
return true;
}
void dfs(int u) {
if (top == s) return (void)(++ans);
if (n - u + top < s) return;
FOR(i, 0, adj[u].size()) {
if (vis[adj[u][i]] || !check(adj[u][i])) continue;
vis[adj[u][i]] = true;
stk[top++] = adj[u][i];
dfs(adj[u][i]);
--top;
vis[adj[u][i]] = false;
}
}
void solve() {
cin >> n >> m >> s;
FOR(i, 0, n) adj[i].clear(), vis[i] = false;
FOR2(i, 0, n, j, 0, n) connected[i][j] = false;
for (int u, v; m; --m) {
cin >> u >> v;
if (--u > --v) swap(u, v);
adj[u].push_back(v);
connected[u][v] = true;
}
ans = 0;
FOR(i, 0, n) {
top = 1;
stk[0] = i;
vis[i] = true;
dfs(i);
}
cout << ans << '
';
}
int main(int argc, char *argv[]) {
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout << fixed << setprecision(10);
int o_o;
for (cin >> o_o; o_o; --o_o) solve();
return 0;
}
G.Do not pour out
题意:给你一个杯子,直径为2,高为2,水高为d,现将杯子倾斜,使得水恰好不洒出,问
此时水表面面积。
显然表面为椭圆或半椭圆,分为两种情况:
- 水面经过杯底,通过二分得出水在杯底的面积,然后计算。
- 水面越过杯底,可以较轻松的算出长短半轴。
#include <bits/stdc++.h>
using namespace std;
const double EPS = 1e-12;
const double PI = acos(-1.0);
inline double cal_v(double x) {
double t = acos(1 - x);
double v = sin(t) - sin(t) * sin(t) * sin(t) / 3 - t * cos(t);
return v * 2 / x;
}
inline double ract(double a, double x) { return (x + sin(2 * x) / 2) * a / 2; }
inline double area(double a, double x) {
return 2 * (ract(a, PI / 2) - ract(a, asin(x / a)));
}
void solve() {
double d;
cin >> d;
if (d == 0) {
cout << .0 << '
';
return;
}
if (d > 1) {
double h = 2 * d - 2;
double a = sqrt(4 + (2 - h) * (2 - h)) / 2;
cout << PI * a << '
';
return;
}
double l = 0, r = 2, v = PI * d;
for (int step = 0; step < 100; ++step) {
double mid = (l + r) / 2;
if (cal_v(mid) > v + EPS) {
r = mid;
} else {
l = mid;
}
}
int sign = l < 1 ? -1 : 1;
double len = sqrt(l * l + 4);
double h = sqrt(2 * l - l * l);
double a = len / (1 + sign * sqrt(1 - h * h));
double x = a - len;
cout << area(a, x) << '
';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout << fixed << setprecision(5);
int o_o;
for (cin >> o_o; o_o; --o_o) solve();
return 0;
}
H.Guessing the Dice Roll
题意:一群人在完骰子游戏,每个人有一个特定的序列,当掷到这个序列时对应的人获胜,
问每个人获胜的概率。
在AC自动机上做概率dp,由于AC自动机不是一个DAG所以要用高斯消元求解。
const int MAX_NODE = 103;
int ch[MAX_NODE][6], fail[MAX_NODE], node_c;
int add_char(int u, int id) {
if (ch[u][id] < 0) ch[u][id] = node_c++;
return ch[u][id];
}
void build_acam() {
queue<int> que;
FOR(i, 0, 6)
if (~ch[0][i]) {
que.push(ch[0][i]);
fail[ch[0][i]] = 0;
} else {
ch[0][i] = 0;
}
while (!que.empty()) {
int u = que.front();
que.pop();
FOR(i, 0, 6)
if (~ch[u][i]) {
que.push(ch[u][i]);
fail[ch[u][i]] = ch[fail[u]][i];
} else {
ch[u][i] = ch[fail[u]][i];
}
}
}
const double EPS = 1e-9;
const int MAXN = MAX_NODE;
double a[MAXN][MAXN], x[MAXN];
int equ, var;
int gauss() {
int i, j, k, col, max_r;
for (k = 0, col = 0; k < equ && col < var; k++, col++) {
max_r = k;
for (i = k + 1; i < equ; i++)
if (fabs(a[i][col]) > fabs(a[max_r][col])) max_r = i;
if (fabs(a[max_r][col]) < EPS) return 0;
if (k != max_r) {
for (j = col; j < var; j++) swap(a[k][j], a[max_r][j]);
swap(x[k], x[max_r]);
}
x[k] /= a[k][col];
for (j = col + 1; j < var; j++) a[k][j] /= a[k][col];
a[k][col] = 1;
for (i = k + 1; i < equ; i++)
if (i != k) {
x[i] -= x[k] * a[i][col];
for (j = col + 1; j < var; j++) a[i][j] -= a[k][j] * a[i][col];
a[i][col] = 0;
}
}
for (col = equ - 1, k = var - 1; ~col; --col, --k) {
if (fabs(a[col][k]) > 0) {
for (i = 0; i < k; ++i) {
x[i] -= x[k] * a[i][col];
for (j = col + 1; j < var; j++) a[i][j] -= a[k][j] * a[i][col];
a[i][col] = 0;
}
}
}
return 1;
}
int ed_node[10], id[MAXN];
void solve() {
int n, l, t;
cin >> n >> l;
node_c = 1;
memset(ch, -1, sizeof(ch));
memset(id, -1, sizeof(id));
FOR(i, 0, n) {
ed_node[i] = 0;
FOR(j, 0, l) cin >> t, ed_node[i] = add_char(ed_node[i], t - 1);
id[ed_node[i]] = i;
}
build_acam();
FOR(step, 0, n) {
equ = var = node_c;
FOR(i, 0, equ) {
x[i] = 0;
FOR(j, 0, var) a[i][j] = 0;
}
FOR(i, 0, node_c) {
a[i][i] = 1;
if (~id[i]) {
x[i] = id[i] == step ? 1 : 0;
} else {
FOR(j, 0, 6) a[i][ch[i][j]] += -1.0 / 6;
}
}
/*
FOR(i, 0, node_c) {
FOR(j, 0, var) cout << a[i][j] << ' ';
cout << x[i] << '
';
}
*/
gauss();
if (step) cout << ' ';
cout << x[0];
}
cout << '
';
}
int main(int argc, char *argv[]) {
// freopen("in.txt", "r", stdin);
ios::sync_with_stdio(false);
cin.tie(nullptr);
cout << fixed << setprecision(6);
int o_o;
for (cin >> o_o; o_o; --o_o) solve();
return 0;
}
I.The Elder
题意:给定一棵带边权树,根节点为1,每个节点要想给根节点通过记者传递信息。记者经
一段路耗时为长度的平方。记者间也可以传递信息,耗时为P,问在最优方案下,一个信息
传递到根节点最长耗时。
定义$ dp_i $ 表示i节点传递到根节点的最短耗时,规定$ dp_{root}=-P (。有如下转移方程 ) dp_u = dp_v + dist(u, v)^2 + P$,v is an ancestor of u.
然后就是在树上做斜率优化。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const int MAXN = 1e5 + 5;
vector<pii> adj[MAXN];
ll dp[MAXN], d[MAXN];
int n, p, q[MAXN], head, tail;
inline ll S(int a, int b) { return (d[b] - d[a]) << 1; }
inline ll G(int a, int b) { return dp[b] - dp[a] + d[b] * d[b] - d[a] * d[a]; }
void dfs(int u, int from) {
vector<int> dhead, dtail;
if (u ^ 1) {
while (head + 2 <= tail &&
S(q[head + 1], q[head]) * d[u] <= G(q[head + 1], q[head]))
dhead.push_back(q[head++]);
int v = q[head];
dp[u] = dp[v] + p + (d[u] - d[v]) * (d[u] - d[v]);
}
while (head + 2 <= tail &&
G(u, q[tail - 1]) * S(q[tail - 1], q[tail - 2]) <=
G(q[tail - 1], q[tail - 2]) * S(u, q[tail - 1]))
dtail.push_back(q[--tail]);
q[tail++] = u;
for (pii &e : adj[u]) {
if (e.first == from) continue;
d[e.first] = d[u] + e.second;
dfs(e.first, u);
}
--tail;
for (int i = dtail.size() - 1; ~i; --i) q[tail++] = dtail[i];
for (int i = dhead.size() - 1; ~i; --i) q[--head] = dhead[i];
}
void solve() {
cin >> n >> p;
for (int i = 1; i <= n; ++i) adj[i].clear();
for (int i = 1, u, v, w; i < n; ++i) {
cin >> u >> v >> w;
adj[u].emplace_back(v, w);
adj[v].emplace_back(u, w);
}
dp[1] = -p;
head = tail = 0;
dfs(1, 1);
ll ans = 0;
for (int i = 1; i <= n; ++i)
if (dp[i] > ans) ans = dp[i];
cout << ans << '
';
}
int main() {
// freopen("in.txt", "r", stdin);
ios::sync_with_stdio(false);
cin.tie(0);
int o_o;
for (cin >> o_o; o_o; --o_o) solve();
return 0;
}