2014-2015 ACM-ICPC, Asia Tokyo Regional Contest
2014-2015 ACM-ICPC, Asia Tokyo Regional Contest
| A | B | C | D | E | F | G | H | I | J | K |
| O | O | O | O | O | O |
签到
#include <bits/stdc++.h> using namespace std; const int N = 20; int b[N], p[N], n, m, cnt[2]; int temp[N]; int put(int opt) { int cnt2[2] = {0}; int index = 0; for (int i = 1; i <= m; i++) { int num = p[i]; while (num--) { temp[++index] = opt; } opt ^= 1; } for (int i = 1; i <= n; i++) cnt2[temp[i]]++; if (cnt2[0] != cnt[0] || cnt2[1] != cnt[1]) return 1e9; int j = 1; int res = 0; for (int i = 1; i <= n; i++) { if (b[i] == 1) { while (temp[j] != 1) j++; res += abs(i - j); j++; } } return res; } int main() { scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) scanf("%d", &b[i]), cnt[b[i]]++; for (int i = 1; i <= m; i++) scanf("%d", &p[i]); printf("%d\n", min(put(0), put(1))); return 0; }
签到
#include <bits/stdc++.h> using namespace std; char s[30]; int res, st[30]; int top; int main() { scanf("%s%d", s + 1, &res); int len = strlen(s + 1); int ans1 = s[1] - '0'; for (int i = 3; i <= len; i += 2) { if (s[i - 1] == '+') { ans1 += s[i] - '0'; } else { ans1 *= s[i] - '0'; } } st[++top] = s[1] - '0'; for (int i = 3; i <= len; i += 2) { if (s[i - 1] == '*') { st[top] *= s[i] - '0'; } else { st[++top] = s[i] - '0'; } } int ans2 = 0; for (int i = 1; i <= top; i++) ans2 += st[i]; if (ans1 == res && ans2 == res) puts("U"); else if (ans1 == res) puts("L"); else if (ans2 == res) puts("M"); else puts("I"); return 0; }
如果两个区间存在包含关系,那么肯定直接走到最大的 $d$ 再走到最小的 $c$ 最优。
如果两个区间交叉,那么之间应该是三段距离,分别设为 $a$, $b$, $c$
如果先走左区间再走右区间,那么走的路程为 $a + b + a + b + a + b + c + b + c + b + c= 3a + 5b + 3c$
如果先走右区间再走坐去间,那么走的路程为 $a + b + c + a + b + c + a + b + c = 3a + 3b + 3c$
所以就是把所有交叉的区间合并成一个大区间,然后每次到区间右端点再走回一遍左端点。
用并查集实现即可。
#include <bits/stdc++.h> using namespace std; const int N = 1007; int fa[N], mx[N], mn[N]; int getfa(int x) { return x == fa[x] ? x : fa[x] = getfa(fa[x]); } void unit(int x, int y) { x = getfa(x), y = getfa(y); if (x == y) return; fa[x] = y; mx[y] = max(mx[y], mx[x]); mn[y] = min(mn[y], mn[x]); } int main() { int n, m; scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) fa[i] = mx[i] = mn[i] = i; while (m--) { int l, r; scanf("%d%d", &l, &r); for (int i = l; i < r; i++) unit(i, i + 1); } int ans = n + 1; for (int i = 1; i <= n; i++) if (getfa(i) == i) ans += (mx[i] - mn[i]) * 2; printf("%d\n", ans); }
高中物理杀我!!!我高考考的最烂的就是物理了!!!
能不能跨过一个板只跟 $v_y$ 有关。枚举一下要弹多少次,然后二分一下 $v_y$,可以求出 $v_x$,得到的 $v_y$ 不一定使合速度最小,因为合速度是一个对勾函数,三分或者判断一下就行。
#include <bits/stdc++.h> using namespace std; const int N = 20; const double eps = 1e-5; int d, n, b; int p[N], h[N]; double loc[N]; int dcmp(double x) { if (fabs(x) < eps) return 0; return x < 0 ? -1 : 1; } bool check(int cnt, double vy) { double per_time = vy * 2.0; double total_time = per_time * cnt; double vx = d / total_time; for (int i = 1; i <= cnt; i++) { double l = loc[i - 1], r = loc[i]; for (int j = 1; j <= n; j++) { if (dcmp(p[j] - l) < 0 || dcmp(p[j] - r) > 0) continue; double cur_time = p[j] / vx; int times = cur_time / per_time; cur_time -= times * per_time; if (dcmp(vy * cur_time - 0.5 * cur_time * cur_time - h[j]) < 0) return 0; } } return 1; } double check0(int cnt, double vy) { double per_time = vy * 2.0; double total_time = per_time * cnt; double vx = d / total_time; return sqrt(vx * vx + vy * vy); } int main() { freopen("in.txt", "r", stdin); scanf("%d%d%d", &d, &n, &b); for (int i = 1; i <= n; i++) scanf("%d%d", p + i, h + i); double ans = 2e4; for (int i = 1; i <= b + 1; i++) { bool flag = 0; if (d % i == 0) { int per = d / i; for (int j = 1; j <= n; j++) if (p[j] % per == 0) { flag = 1; break; } } if (flag) continue; double per = 1.0 * d / i; for (int j = 1; j <= i; j++) loc[j] = per * j; double l = 0, r = 2e4; for (int cnt = 0; cnt < 100; cnt++) { double mid = (l + r) / 2.0; if (check(i, mid)) r = mid; else l = mid; } r = 2e4; for (int cnt = 0; cnt < 100; cnt++) { double ll = l + (r - l) / 3.0, rr = r - (r - l) / 3.0; if (check0(i, ll) < check0(i, rr)) r = rr; else l = ll; } ans = min(ans, check0(i, l)); } printf("%.5f\n", ans); return 0; }
这 $n$ 也出得太小了!!!本质就是求哪些边一定存在于所有最小生成树上。那么就是CF原题了。
#include <bits/stdc++.h> using namespace std; const int N = 1e5 + 7; int n, m; struct Edge { int u, v, cost, id; bool operator < (const Edge &rhs) const { return cost < rhs.cost; } } edge[N], edge2[N]; struct E { int v, ne, id; } e[N]; int head[N], cnt, ans[N]; inline void add(int u, int v, int id) { e[cnt] = (E){v, head[u], id}; head[u] = cnt++; e[cnt] = (E){u, head[v], id}; head[v] = cnt++; } int fa[N], dfn[N], low[N], tol; int getfa(int x) { return x == fa[x] ? x : fa[x] = getfa(fa[x]); } void dfs(int u, int id) { low[u] = dfn[u] = ++tol; for (int i = head[u]; ~i; i = e[i].ne) { int v = e[i].v; if (!dfn[v]) { dfs(v, i); low[u] = min(low[u], low[v]); if (low[v] > dfn[u]) ans[e[i].id] = 0; } else if (i != (id ^ 1)) { low[u] = min(low[u], dfn[v]); } } } inline void unite(int u, int v) { u = getfa(u); v = getfa(v); if (u != v) fa[u] = v; } int main() { //freopen("in.txt", "r", stdin); scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) fa[i] = i; for (int i = 0; i < m; i++) { scanf("%d%d%d", &edge[i].u, &edge[i].v, &edge[i].cost); edge[i].id = i; } memcpy(edge2, edge, sizeof(edge2)); sort(edge, edge + m); for (int i = 0; i < m; ) { int j = i; while (j + 1 < m && edge[j + 1].cost == edge[j].cost) j++; cnt = tol = 0; for (int k = i; k <= j; k++) { edge[k].u = getfa(edge[k].u); edge[k].v = getfa(edge[k].v); int u = edge[k].u, v = edge[k].v; head[u] = head[v] = -1; dfn[u] = dfn[v] = low[u] = low[v] = 0; } for (int k = i; k <= j; k++) { int u = edge[k].u, v = edge[k].v, id = edge[k].id; if (u == v) { //printf("%d\n", k); ans[id] = 2; continue; } ans[id] = 1; add(u, v, id); } for (int k = i; k <= j; k++) if (!dfn[edge[k].u]) dfs(edge[k].u, -1); for (; i <= j; ++i) unite(edge[i].u, edge[i].v); } int ans1 = 0; long long ans2 = 0; for (int i = 0; i < m; i++) if (ans[i] == 0) ans1++, ans2 += edge2[i].cost; printf("%d %lld\n", ans1, ans2); return 0; }
这道题能写出来是因为大一上学期刚学C语言接触了括号序列判定是否合法的题。当时还没学栈,所以想了一个做法。把左括号看成 +$1$,右括号看成 -$1$,每个点表示当前的前缀和。
那么序列合法等价于,前缀和数组没有位置小于0,且最后一个数等于0。
对于这个题,在位置 $x$ 修改一个括号,就相当于区间 $[x, n]$ 加(减)2。
如果把一个左括号变成右括号,相当于区间 $-2$,那么只需要找当最左边的 $-1$位置,使它加 $2$ 就能使序列合法。
如果把一个右括号变成左括号,相当于区间 $+2$,那么找到第一个前缀和大于 $1$ 的位置,从他之后第一个 $1$ 变成 $-1$ 即可,这里我选择二分找那个位置。应该有1log的做法。
#include <bits/stdc++.h> using namespace std; const int N = 3e5 + 7; int x[N], sum[N], n; char s[N]; struct Seg { #define lp p << 1 #define rp p << 1 | 1 int tree[N << 2][2]; void pushup(int p) { tree[p][0] = min(tree[lp][0], tree[rp][0]); tree[p][1] = min(tree[lp][1], tree[rp][1]); } void build(int p, int l, int r) { if (l == r) { if (l == 1 || l == n) { tree[p][0] = tree[p][1] = N; return; } if (x[l] == 1) tree[p][0] = l, tree[p][1] = N; else tree[p][0] = N, tree[p][1] = l; return; } int mid = l + r >> 1; build(lp, l, mid); build(rp, mid + 1, r); pushup(p); } void update(int p, int l, int r, int pos) { if (l == r) { swap(tree[p][0], tree[p][1]); return; } int mid = l + r >> 1; if (pos <= mid) update(lp, l, mid, pos); else update(rp, mid + 1, r, pos); pushup(p); } int query(int p, int l, int r, int x, int y, int opt) { if (x <= l && y >= r) return tree[p][opt]; int mid = l + r >> 1; int ans = N; if (x <= mid) ans = min(ans, query(lp, l, mid, x, y, opt)); if (y > mid) ans = min(ans, query(rp, mid + 1, r, x, y, opt)); return ans; } } seg; struct Seg1 { #define lp p << 1 #define rp p << 1 | 1 int tree[N << 2], lazy[N << 2]; void pushup(int p) { tree[p] = min(tree[lp], tree[rp]); } void build(int p, int l, int r) { if (l == r) { tree[p] = sum[l]; return; } int mid = l + r >> 1; build(lp, l, mid); build(rp, mid + 1, r); pushup(p); } void tag(int p, int val) { tree[p] += val; lazy[p] += val; } void pushdown(int p) { if (lazy[p]) { tag(lp, lazy[p]); tag(rp, lazy[p]); lazy[p] = 0; } } void update(int p, int l, int r, int x, int y, int val) { if (x <= l && y >= r) { tree[p] += val; lazy[p] += val; return; } int mid = l + r >> 1; pushdown(p); if (x <= mid) update(lp, l, mid, x, y, val); if (y > mid) update(rp, mid + 1, r, x, y, val); pushup(p); } int query(int p, int l, int r, int x, int y) { if (x <= l && y >= r) return tree[p]; int mid = l + r >> 1; pushdown(p); int ans = N; if (x <= mid) ans = min(ans, query(lp, l, mid, x, y)); if (y > mid) ans = min(ans, query(rp, mid + 1, r, x, y)); return ans; } void print(int p, int l, int r){ if (l == r){ printf("%d ", tree[p]); return; } int mid = l + r >> 1; pushdown(p); print(lp, l, mid); print(rp, mid + 1, r); } } seg1; int get(char ch) { if (ch == '(') return 1; return -1; } int main() { freopen("in.txt", "r", stdin); int q; scanf("%d%d", &n, &q); scanf("%s", s + 1); for (int i = 1; i <= n; i++) x[i] = get(s[i]), sum[i] = sum[i - 1] + x[i]; seg.build(1, 1, n); seg1.build(1, 1, n); for (int u; q--; ) { scanf("%d", &u); if (u == 1 || u == n) { printf("%d\n", u); continue; } seg.update(1, 1, n, u); if (x[u] == 1) seg1.update(1, 1, n, u, n, -2); else seg1.update(1, 1, n, u, n, 2); x[u] *= -1; int opt = (x[u] == 1 ? 0 : 1); if (opt == 1) { int pos = seg.tree[1][opt]; printf("%d\n", pos); x[pos] *= -1; seg.update(1, 1, n, pos); seg1.update(1, 1, n, pos, n, 2); } else { int l = 1, r = n; int temp = 0; while (l <= r) { int mid = l + r >> 1; if (seg1.query(1, 1, n, mid, n) >= 2) r = mid - 1, temp = mid; else l = mid + 1; } int pos = seg.query(1, 1, n, temp, n, opt); printf("%d\n", pos); x[pos] *= -1; seg.update(1, 1, n, pos); seg1.update(1, 1, n, pos, n, -2); } } return 0; }
还真有1log的
相当于查区间最后一个小于2的位置,那么先查右子树,如果右子树最小值小于2,就返回右,否则返回左。
#include <bits/stdc++.h> using namespace std; const int N = 3e5 + 7; int x[N], sum[N], n; char s[N]; struct Seg { #define lp p << 1 #define rp p << 1 | 1 int tree[N << 2][2]; void pushup(int p) { tree[p][0] = min(tree[lp][0], tree[rp][0]); tree[p][1] = min(tree[lp][1], tree[rp][1]); } void build(int p, int l, int r) { if (l == r) { if (l == 1 || l == n) { tree[p][0] = tree[p][1] = N; return; } if (x[l] == 1) tree[p][0] = l, tree[p][1] = N; else tree[p][0] = N, tree[p][1] = l; return; } int mid = l + r >> 1; build(lp, l, mid); build(rp, mid + 1, r); pushup(p); } void update(int p, int l, int r, int pos) { if (l == r) { swap(tree[p][0], tree[p][1]); return; } int mid = l + r >> 1; if (pos <= mid) update(lp, l, mid, pos); else update(rp, mid + 1, r, pos); pushup(p); } int query(int p, int l, int r, int x, int y, int opt) { if (x <= l && y >= r) return tree[p][opt]; int mid = l + r >> 1; int ans = N; if (x <= mid) ans = min(ans, query(lp, l, mid, x, y, opt)); if (y > mid) ans = min(ans, query(rp, mid + 1, r, x, y, opt)); return ans; } } seg; struct Seg1 { #define lp p << 1 #define rp p << 1 | 1 int tree[N << 2], lazy[N << 2]; void pushup(int p) { tree[p] = min(tree[lp], tree[rp]); } void build(int p, int l, int r) { if (l == r) { tree[p] = sum[l]; return; } int mid = l + r >> 1; build(lp, l, mid); build(rp, mid + 1, r); pushup(p); } void tag(int p, int val) { tree[p] += val; lazy[p] += val; } void pushdown(int p) { if (lazy[p]) { tag(lp, lazy[p]); tag(rp, lazy[p]); lazy[p] = 0; } } void update(int p, int l, int r, int x, int y, int val) { if (x <= l && y >= r) { tree[p] += val; lazy[p] += val; return; } int mid = l + r >> 1; pushdown(p); if (x <= mid) update(lp, l, mid, x, y, val); if (y > mid) update(rp, mid + 1, r, x, y, val); pushup(p); } int query(int p, int l, int r) { if (tree[p] > 1) return l; if (l == r) return l; int mid = l + r >> 1; pushdown(p); if (tree[rp] <= 1) return query(rp, mid + 1, r); return query(lp, l, mid); } void print(int p, int l, int r){ if (l == r){ printf("%d ", tree[p]); return; } int mid = l + r >> 1; pushdown(p); print(lp, l, mid); print(rp, mid + 1, r); } } seg1; int get(char ch) { if (ch == '(') return 1; return -1; } int main() { freopen("in.txt", "r", stdin); int q; scanf("%d%d", &n, &q); scanf("%s", s + 1); for (int i = 1; i <= n; i++) x[i] = get(s[i]), sum[i] = sum[i - 1] + x[i]; seg.build(1, 1, n); seg1.build(1, 1, n); for (int u; q--; ) { scanf("%d", &u); if (u == 1 || u == n) { printf("%d\n", u); continue; } seg.update(1, 1, n, u); if (x[u] == 1) seg1.update(1, 1, n, u, n, -2); else seg1.update(1, 1, n, u, n, 2); x[u] *= -1; int opt = (x[u] == 1 ? 0 : 1); if (opt == 1) { int pos = seg.tree[1][opt]; printf("%d\n", pos); x[pos] *= -1; seg.update(1, 1, n, pos); seg1.update(1, 1, n, pos, n, 2); } else { int l = 1, r = n; int temp = seg1.query(1, 1, n) + 1; int pos = seg.query(1, 1, n, temp, n, opt); printf("%d\n", pos); x[pos] *= -1; seg.update(1, 1, n, pos); seg1.update(1, 1, n, pos, n, -2); } } return 0; }

浙公网安备 33010602011771号