Avito Code Challenge 2018
A. Antipalindrome
直接暴力判断就行了。
#include <bits/stdc++.h> using namespace std; const int N = 100; char s[N]; bool check(int i, int j) { while (i <= j) { if (s[i] != s[j]) return 0; i++; j--; } return 1; } int main() { scanf("%s", s); int n = strlen(s); for (int tail = n - 1; tail >= 0; tail--) { if (!check(0, tail)) { printf("%d\n", tail + 1); return 0; } } puts("0"); return 0; }
B. Businessmen Problems
排序或者map都行。
#include <bits/stdc++.h> #define fi first #define se second using namespace std; const int N = 1e5 + 7; map<int, int> mp; int main() { int n; scanf("%d", &n); long long ans = 0; for (int i = 1; i <= n; i++) { int x, y; scanf("%d%d", &x, &y); mp[x] = y; ans += y; } int m; scanf("%d", &m); for (int i = 1; i <= m; i++) { int x, y; scanf("%d%d", &x, &y); if (mp.count(x)) { if (mp[x] < y) ans += y - mp[x]; } else ans += y; } printf("%lld\n", ans); return 0; }
C. Useful Decomposition
如果整棵树没有一个节点度数大于 $2$,那么显然这棵树就是一条链,输出两个度数为 $1$ 的节点即可。
存在度数大于 $2$ 的,那么这个节点就是那个分解点,看有多少个度数大于等于 $3$ 即可。
#include <bits/stdc++.h> using namespace std; const int N = 1e5 + 7; vector<int> G[N]; int main() { int n; scanf("%d", &n); for (int i = 1, u, v; i < n; i++) { scanf("%d%d", &u, &v); G[u].push_back(v); G[v].push_back(u); } vector<int> ends; int ans = 0; for (int i = 1; i <= n; i++) { if (G[i].size() >= 3) { if (!ans) { ans = i; } else { puts("No"); return 0; } } else if (G[i].size() == 1) { ends.push_back(i); } } puts("Yes"); if (!ans) { printf("1\n%d %d\n", ends[0], ends[1]); } else { printf("%d\n", (int)ends.size()); for (auto v: ends) printf("%d %d\n", ans, v); } return 0; }
D. Bookshelves
这是第二次见位运算参与的DP了。
位运算不满足最优子结构的性质,当前是max,不一定后面用到这个值能使答案更大。
那么考虑按位贪心,位越高为 $1$ 肯定越优。
那么从高到低枚举每一位能否为 $1$,再用DP check一下即可。
#include <bits/stdc++.h> #define ll long long using namespace std; const int N = 60; bool dp[N][N]; ll sum[N]; int n, m; bool check(ll ans) { memset(dp, 0, sizeof(dp)); dp[0][0] = 1; for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) for (int k = 1; k <= i; k++) if (((sum[i] - sum[k - 1] & ans) == ans) && dp[k - 1][j - 1]) { dp[i][j] = 1; break; } return dp[n][m]; } int main() { scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) scanf("%lld", &sum[i]), sum[i] += sum[i - 1]; long long ans = 0; for (int i = 58; ~i; i--) { if (check(ans | (1LL << i))) ans |= (1LL << i); } printf("%lld\n", ans); return 0; }
E. Addition on Segments
写了一个可撤销的DP。没有意识到会爆long long。WA在75。
一看题解发现加个不容易被卡的模数可行。还真过了。板子里面得备一些奇奇怪怪的模数了。
最开始写的很暴力。扫描线,遇到一个 $l$ 当前multiset就插入这个 $x$,遇到一个 $r$ 就删除。
如果当前位置的值都处理完了,multiset不为空,就做一遍DP,用bitset优化一下,但是显然复杂度能卡到 $O(\dfrac{n ^ 3}{32})$
然后就想到每次都没必要初始化背包,加入一个数就直接加,删除就直接删,也就是正着做减法背包。
复杂度 $O(nq)$。但就是爆long long。加个模数就OK了。
#include <bits/stdc++.h> #define pii pair<int, int> #define fi first #define se second using namespace std; const int N = 1e4 + 1; const int MOD = 402653189; bitset<N> mask, temp; vector<pii> p[N]; int dp[N]; void M(int &x) { if (x >= MOD) x -= MOD; if (x < 0) x += MOD; } int main() { int n, q; scanf("%d%d", &n, &q); for (int i = 1; i <= q; i++) { int l, r, x; scanf("%d%d%d", &l, &r, &x); p[l].push_back(pii(x, 1)); p[r + 1].push_back(pii(x, -1)); } dp[0] = 1; for (int i = 1; i <= n; i++) { for (auto u: p[i]) { if (u.se == -1) { for (int i = u.fi; i <= n; i++) { M(dp[i] -= dp[i - u.fi]); if (dp[i]) temp.set(i, 1); else temp.set(i, 0); } } else { for (int i = n; i >= u.fi; i--) { M(dp[i] += dp[i - u.fi]); if (dp[i]) temp.set(i, 1); else temp.set(i, 0); } } } mask |= temp; } vector<int> ans; for (int i = 1; i <= n; i++) if (mask.test(i)) ans.push_back(i); int cnt = 0; cnt = ans.size(); printf("%d\n", cnt); for (auto x: ans) { printf("%d%c", x, " \n"[cnt == 1]); cnt--; } return 0; }
本质就是维护每个节点的数形成的背包长啥样。长啥样具体可以用bitset表示。
那么就用线段树维护bitset。下放标记就行了。
复杂度 $O(\dfrac{nqlogn}{32})$
#include <bits/stdc++.h> using namespace std; const int N = 1e4 + 5; bitset<N> ans; struct Seg { #define lp p << 1 #define rp p << 1 | 1 bitset<N> tree[N * 4]; vector<int> lazy[N * 4]; void init() { for (int i = 1; i < 4 * N; i++) tree[i] = 1; } void update(int p, int l, int r, int x, int y, int v) { if (x <= l && y >= r) { lazy[p].push_back(v); return; } int mid = l + r >> 1; if (x <= mid) update(lp, l, mid, x, y, v); if (y > mid) update(rp, mid + 1, r, x, y, v); } void pushdown(int p) { tree[lp] |= tree[p]; tree[rp] |= tree[p]; } void build(int p, int l, int r) { for (auto x: lazy[p]) { tree[p] |= (tree[p] << x); } if (l == r) { ans |= tree[p]; return; } pushdown(p); int mid = l + r >> 1; build(lp, l, mid); build(rp, mid + 1, r); } } seg; int main() { int n, q; scanf("%d%d", &n, &q); seg.init(); for (int i = 1; i <= q; i++) { int l, r, x; scanf("%d%d%d", &l, &r, &x); seg.update(1, 1, n, l, r, x); } seg.build(1, 1, n); int cnt = 0; for (int i = 1; i <= n; i++) if (ans.test(i)) cnt++; printf("%d\n", cnt); for (int i = 1; i <= n; i++) if (ans.test(i)) printf("%d ", i); return 0; }
F. Round Marriage
二分答案。
环上距离表示为 $min(|a_i - b_j|, L - |a_i - b_j|)$。
拆绝对值就发现只多了 $b_j + L$, $b_j - L$ 这两项。
那么加入到 $b$ 数组里面就可以破环成链了。
把 $a$ $b$ 数组排序
如果 $a_1$ 选了 $b_j$,那么接下来肯定是 $a_2$ 选 $b_{j+1}$ 依次下去肯定是最优的。
所以就看每个 $a_i$ 能在 $b$ 匹配的区间中,能否每个区间取一个数形成一个等差数列。
一种实现方式是对于每一个 $a_i$ 能交的区间 $[l, r]$,左右端点减去 $i$ 变成 $[l - i, r - i]$。就变成了求有没有起点相同。
直接求区间交就可以了。
这个贪心是最优的但不是充要的。
第一个数求到了 $[l, r]$,那么第二个数从 $[l + 1, r + 1]$ 里面找一个合法的区间,这样继续找,只要最后一个数能找的区间存在,那么就是可以的。
双指针扫就行了。
#include <bits/stdc++.h> using namespace std; const int N = 2e5 + 7; int n, a[N], b[N * 4]; bool check(int x) { int l = 1, r = 3 * n; for (int i = 1; i <= n; i++) { while (a[i] - b[l] > x) l++; while (b[r] - a[i] > x) r--; l++; r++; } return l <= r; } int main() { int L; scanf("%d%d", &n, &L); for (int i = 1; i <= n; i++) scanf("%d", a + i); for (int i = 1; i <= n; i++) scanf("%d", b + i), b[n + i] = b[i] + L, b[2 * n + i] = b[i] - L; sort(a + 1, a + n + 1); sort(b + 1, b + 1 + 3 * n); int l = 0, r = L, ans = 0; while (l <= r) { int mid = l + r >> 1; if (check(mid)) ans = mid, r = mid - 1; else l = mid + 1; } printf("%d\n", ans); return 0; }
G. Magic multisets
首先对整个区间都乘上 $2$,再对那些没有这个数的乘上 $2$ 的逆元再加 $1$。
第二个步骤是关键。
开 $N$ 个 set,对于每个数 $x$ 的set存一些区间,表示这些区间里都有这个数 $x$。
每次更新操作的区间 $[l, r]$,就相当于把这个set里面与 $[l, r]$ 相交的区间都合并起来。
意会一下感觉总的合并次数最多只有 $n$ 次。
#include <bits/stdc++.h> #define pii pair<int, int> #define fi first #define se second using namespace std; const int N = 2e5 + 7; const int MOD = 998244353; const int inv2 = (MOD + 1) / 2; int n, q; int M(int x) { return x >= MOD ? x - MOD : x; } struct Seg { #define lp p << 1 #define rp p << 1 | 1 int tree[N * 4], mul[N * 4], add[N * 4]; void build(int p, int l, int r) { mul[p] = 1; if (l == r) return; int mid = l + r >> 1; build(lp, l, mid); build(rp, mid + 1, r); } inline void pushup(int p) { tree[p] = M(tree[lp] + tree[rp]); } inline void tagm(int p, int val) { mul[p] = 1LL * mul[p] * val % MOD; add[p] = 1LL * add[p] * val % MOD; tree[p] = 1LL * tree[p] * val % MOD; } inline void taga(int p, int val, int cnt) { add[p] = M(add[p] + val); tree[p] = M(tree[p] + 1LL * cnt * val % MOD); } inline void pushdown(int p, int llen, int rlen) { if (mul[p] != 1) { tagm(lp, mul[p]); tagm(rp, mul[p]); mul[p] = 1; } if (add[p]) { taga(lp, add[p], llen); taga(rp, add[p], rlen); add[p] = 0; } } void update_M(int p, int l, int r, int x, int y, int val) { if (x <= l && y >= r) { tree[p] = 1LL * tree[p] * val % MOD; mul[p] = 1LL * mul[p] * val % MOD; add[p] = 1LL * add[p] * val % MOD; return; } int mid = l + r >> 1; pushdown(p, mid - l + 1, r - mid); if (x <= mid) update_M(lp, l, mid, x, y, val); if (y > mid) update_M(rp, mid + 1, r, x, y, val); pushup(p); } void update_A(int p, int l, int r, int x, int y, int val) { if (x <= l && y >= r) { tree[p] = M(tree[p] + 1LL * (r - l + 1) * val % MOD); add[p] = M(add[p] + val); return; } int mid = l + r >> 1; pushdown(p, mid - l + 1, r - mid); if (x <= mid) update_A(lp, l, mid, x, y, val); if (y > mid) update_A(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; int ans = 0; pushdown(p, mid - l + 1, r - mid); if (x <= mid) ans = M(ans + query(lp, l, mid, x, y)); if (y > mid) ans = M(ans + query(rp, mid + 1, r, x, y)); return ans; } } seg; set<pii> st[N]; void update(int l, int r, int x) { seg.update_M(1, 1, n, l, r, 2); set<pii>::iterator it; for (it = st[x].lower_bound(pii(l, l)); it != st[x].end(); it++) { set<pii>::iterator last = it; last--; int L = (*last).se + 1, R = (*it).fi - 1; L = max(L, l); R = min(R, r); if (L <= R) { seg.update_M(1, 1, n, L, R, inv2); seg.update_A(1, 1, n, L, R, 1); } if ((*it).fi >= r) break; } it = st[x].upper_bound(pii(l, l)); it--; pii p = *it; int L = l, R = r; if (p.se >= L) L = p.fi; it = st[x].upper_bound(pii(r, r)); it--; p = *it; if (p.se >= R) R = p.se; vector<pii> vec; for (it = st[x].lower_bound(pii(L, L)); it != st[x].end(); it++) { p = *it; if (p.fi >= L && p.se <= R) vec.push_back(p); else break; } for (auto p : vec) st[x].erase(p); st[x].insert(pii(L, R)); } int main() { //freopen("in.txt", "r", stdin); scanf("%d%d", &n, &q); seg.build(1, 1, n); for (int i = 0; i <= n; i++) st[i].insert(pii(0, 0)), st[i].insert(pii(n + 1, n + 1)); for (int i = 1, opt, l, r, x; i <= q; i++) { scanf("%d%d%d", &opt, &l, &r); if (opt == 1) { scanf("%d", &x); update(l, r, x); } else { printf("%d\n", seg.query(1, 1, n, l, r)); } } return 0; }