AtCoder Beginner Contest 350
A - Past ABCs
Solution
把最後三個字元轉成數字判斷即可
Code
#include <bits/stdc++.h>
using namespace std;
int main() {
string s; cin >> s;
s = s.substr(3,3);
int x = 0;
x = (s[0] -'0') * 100 + (s[1] - '0') * 10 + (s[2] - '0');
if (x >= 350)
return cout << "No" << endl, 0;
if (x == 316)
return cout << "No" << endl, 0;
if (x <= 0)
return cout << "No" << endl, 0;
cout << "Yes" << endl;
return 0;
}
B - Dentist Aoki
Solution
按照題意模擬
Code
#include <bits/stdc++.h>
using namespace std;
int main() {
int n, m;
cin >> n >> m;
vector<int> p (n + 1, 1);
for (int i = 1; i <= m; i++) {
int x; cin >> x;
p[x] ^= 1;
}
int ans = 0;
for (int i = 1; i <= n; i++)
ans += p[i];
cout << ans << endl;
return 0;
}
C - Sort
Solution
記錄 \(A[i]\) 已經每個數對應的位置 \(p[x]\) ,顯然 \(pos[A[i]]=i\)
我們每次操作就是把第 \(i\) 小的數換到 \(i\) 位置
也就是位置 \(i\) 和位置 \(p[i]\)
交換之後更新 \(A\) 陣列和 \(p\) 陣列
Code
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> pii;
vector<pii> ans;
int main() {
int n; cin >> n;
vector<int> p(n + 1), a(n + 1);
for (int i = 1; i <= n; i++) {
cin >> a[i];
p[a[i]] = i;
}
for (int i = 1; i <= n; i++) {
if (a[i] == i) continue;
int pos = p[i];
swap(a[pos], a[i]);
ans.push_back({pos, i});
p[a[pos]] = pos;
p[a[i]] = i;
}
cout << ans.size() << endl;
for (auto &x : ans) {
if (x.first > x.second)
swap(x.first, x.second);
cout << x.first << " " << x.second << endl;
}
return 0;
}
D - New Friends
Solution
顯然,透過任意此操作後,任何一個原來在同一連通塊的點直接都會有一條邊
所以,對於一個連通塊,需要新增的邊數就是 \(n\times (n-1)/2 -\) 已經存在的邊數,其中 \(n\) 為連通塊內點的個數
判斷連通塊可以用並查集來實現
Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
int main() {
int N, M; cin >> N >> M;
vector<vector<int> > g(N + 1);
vector<pii> edges;
for (int i = 1; i <= M; i++) {
int A, B; cin >> A >> B;
edges.push_back({A, B});
}
vector<int> fa(N + 1);
iota(fa.begin(), fa.end(), 0);
function<int(int)> find = [&](int x) {
return fa[x] == x ? x : fa[x] = find(fa[x]);
};
for (auto &[A, B] : edges) {
int x = find(A), y = find(B);
if (x != y) fa[x] = y;
}
vector<int> cnt_x(N + 1, 0), cnt_y(N + 1, 0);
for (int i = 1; i <= N; i++)
cnt_x[find(i)]++;
for (auto &[A, B] : edges)
cnt_y[find(A)]++;
ll ans = 0;
for (int i = 1; i <= N; i++) if (i == fa[i]) {
ans += 1ll * cnt_x[i] * (cnt_x[i] - 1) / 2 - cnt_y[i];
}
cout << ans << endl;
return 0;
}
E - Toward 0
Solution
一個比較顯然的機率 DP
定義 \(dp[i]\) 表示把 \(i\) 變成 \(0\) 的最小花費
第一種方法:
第二種方法:
移項得:
兩者中選小的轉移
Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef double ld;
ll N, A, X, Y;
map<ll, ld> dp;
const ld p = 1 / 6.0;
ld f(ll T) {
if (T == 0) return 0;
if (dp.count(T)) return dp[T];
ld ans1 = f(T / A) + X;
ld ans2 = 1.2 * ( (f(T / 2) + f(T / 3) + f(T / 4) + f(T / 5) + f(T / 6)) * p + Y);
return dp[T] = min(ans1, ans2);
}
int main() {
cin >> N >> A >> X >> Y;
printf ("%.10lf\n", f(N));
return 0;
}
F - Transpose
Solution
我們可以把大小寫和翻轉作為兩種獨立的操作來考慮
對於大小寫,利用樹狀陣列記錄一個數字被改變大小寫了多少次,如果是偶數則不動,如果是奇數次則轉變大小寫
對於翻轉操作,可以用 Splay + 標記維護
Code
#include <bits/stdc++.h>
using namespace std;
struct Node {
int fa, ch[2], tag, siz;
};
vector<Node> t;
int rt;
bool ident (int x, int f) { // 判斷 x 是 f 的左兒子還是右兒子
return t[f].ch[1] == x;
}
void connect (int x, int f, int op) {
t[f].ch[op] = x;
t[x].fa = f;
}
void push_up (int x) {
t[x].siz = t[t[x].ch[0]].siz + t[t[x].ch[1]].siz + 1;
}
void push_down (int x) {
if (t[x].tag) {
swap (t[x].ch[0], t[x].ch[1]);
t[t[x].ch[0]].tag ^= 1;
t[t[x].ch[1]].tag ^= 1;
t[x].tag = 0;
}
}
void build (int l, int r, int f) {
if (l > r) return ;
int mid = (l + r) >> 1;
if (mid < f) t[f].ch[0] = mid;
else t[f].ch[1] = mid;
t[mid].siz = 1; t[mid].fa = f;
if (l == r) return ;
build(l, mid - 1, mid); build(mid + 1, r, mid);
push_up(mid);
}
void rotate (int x) {
int f = t[x].fa, g = t[f].fa, k = ident(x, f);
connect (x, g, ident(f, g));
connect (t[x].ch[k ^ 1], f, k);
connect (f, x, k ^ 1);
push_up(f); push_up(x);
}
void splay (int x, int top) {
if (!top) rt = x;
while (t[x].fa != top) {
int f = t[x].fa, g = t[f].fa;
if (g != top)
ident(x, f) ^ ident(f, g) ? rotate(x) : rotate(f);
rotate(x);
}
}
void rever (int L, int R) {
splay(L, 0); splay(R, L);
t[t[R].ch[0]].tag ^= 1;
}
int find (int x, int k) { // 找到以 x 為根的樹中第 k 個節點
push_down(x);
int sum = t[t[x].ch[0]].siz + 1;
if (sum == k) return x;
if (sum > k) return find(t[x].ch[0], k);
else return find(t[x].ch[1], k - sum);
}
void inorder (int x, vector<int> & ord) {
push_down(x);
if (t[x].ch[0]) inorder(t[x].ch[0], ord);
if (x >= 2 && x <= t.size() - 2)
ord.push_back(x - 1);
if (t[x].ch[1]) inorder(t[x].ch[1], ord);
}
vector<int> c;
void add_x (int x, int v) {
for (int i = x; i < c.size(); i += i & -i)
c[i] += v;
}
int query (int x) {
int res = 0;
for (int i = x; i; i -= i & -i)
res += c[i];
return res;
}
int main() {
freopen ("F.in", "r", stdin);
string s; cin >> s; s = " " + s;
int n = s.size() - 1;
t.resize(n + 3); c.resize(n + 1);
rt = (n + 3) / 2;
build(1, rt - 1, rt); build(rt + 1, n + 2, rt);
stack<int> st;
for (int i = 1; i <= n; i++) {
if (s[i] == '(')
st.push(i);
else if (s[i] == ')'){
int l = st.top(), r = i; st.pop();
add_x(l, 1); add_x(r + 1, -1);
int L = find (rt, l), R = find(rt, r + 2);
rever(L, R);
}
}
vector<int> ord;
inorder(rt, ord);
for (auto x : ord) {
if (s[x] == '(' || s[x] == ')') continue;
int v = query(x);
if (v & 1) {
if ('A' <= s[x] && s[x] <= 'Z')
s[x] = s[x] - 'A' + 'a';
else
s[x] = s[x] - 'a' + 'A';
}
cout << s[x];
}
return 0;
}
G - Mediator
Solution
由於強制線上,沒法亂搞
我們記錄下每個節點的父親節點 \(fa[x]\),顯然,透過 \(fa[x]\) 我們可以得出詢問的答案
考慮如果維護 \(fa[x]\)
如果把 \(u\) 和 \(v\) 連線起來
不妨設 \(u\) 是 \(v\) 的父親
由於一棵樹的任意一個點當根節點仍然保持者樹的性質,所以我們讓 \(v\) 作為右邊那棵樹的根節點,然後把根節點接到 \(u\) 上面,完成合並
在轉換根節點的時候,\(v\) 所在的樹的 \(fa\) 也發生了改變,具體發生改變的點是 \(v\) 到原來根節點路徑上的所有點,改變方式是調換了位置,子節點和父節點發生了調換
這樣的調換最多出現 \(v\) 的子樹次,暴力修改會超時
想到啟發式合併,每次 \(v\) 作為較的子樹,\(u\) 作為較大的樹,所以總時間複雜度控制在 \(O(n\log n)\)
Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll TT = 998244353;
vector<int> fa, siz;
int find (int x) {
if (fa[x] == x) return x;
return fa[x] = find(fa[x]);
}
vector<int> real_fa;
int main() {
freopen ("G.in", "r", stdin);
int n, m; cin >> n >> m;
fa.resize(n + 1); siz.resize(n + 1); real_fa.resize(n + 1);
for (int i = 1; i <= n; i++) {
fa[i] = i;
siz[i] = 1;
real_fa[i] = 0;
};
int lst = 0;
while (m--) {
ll a, b, c; cin >> a >> b >> c;
ll A = 1 + (((a * (1ll + lst)) % TT) % 2);
ll B = 1 + (((b * (1ll + lst)) % TT) % n);
ll C = 1 + (((c * (1ll + lst)) % TT) % n);
int op = A, u = B, v = C;
// int op, u, v; cin >> op >> u >> v;
if (op == 1) {
if (siz[find(u)] < siz[find(v)]) swap(u, v);
siz[find(u)] += siz[find(v)]; fa[find(v)] = find(u);
int nxt = real_fa[v]; real_fa[v] = u;
for (int s = v, f = nxt; f;) {
int nxt = real_fa[f];
real_fa[f] = s;
s = f; f = nxt;
}
}
else if (op == 2) {
if (find(u) != find(v)) {
cout << (lst = 0) << '\n';
}
else {
auto check = [&](int u, int v) {
if (u == real_fa[real_fa[v]]) return real_fa[v];
if (real_fa[u] == real_fa[v]) return real_fa[u];
return 0;
};
lst = check(u, v) | check(v, u);
cout << lst << '\n';
}
}
}
return 0;
}