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\) 的最小花费
第一种方法:
\[dp[i]=dp[\lfloor \frac{i}{A}\rfloor]+X \]第二种方法:
\[dp[i]=\frac{1}{6}(dp[i] + dp[\lfloor \frac{i}{2}\rfloor]+\cdots+ dp[\lfloor \frac{i}{6}\rfloor]) +Y \]移项得:
\[dp[i]=\frac{6}{5}\times (\frac{1}{6}\times(dp[\lfloor \frac{i}{2}\rfloor]+\cdots+ dp[\lfloor \frac{i}{6}\rfloor])+Y) \]两者中选小的转移
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;
}
标签:AtCoder,ch,return,int,题解,fa,350,find,dp
From: https://www.cnblogs.com/martian148/p/18153587