目录
Codeforces Round #446 (Div. 2)
- 总体:rating涨了好多,虽然有部分是靠和一些大佬(例如 redbag 和 Hometown )交流的……希望下次能自己做出来2333
A. Greed
题意
给你\(n\)个罐子,每个罐子都告诉了你它当前的饮料体积和它的总容积,问能否用两个罐子装下所有饮料(\(n\le 100000\))
题解
简单模拟即可,用两个最大的算一下,注意累和会爆\(int\)
int a[100100], b[100100];long long sum = 0;int main () { int n = read(); For (i, 1, n) { a[i] = read(); sum += a[i]; } For (i, 1, n) b[i] = read(); sort (b + 1, b + 1 + n); if (b[n] + b[n - 1] >= sum) puts("YES"); else puts("NO"); return 0;}
B. Wrath
题意
给你一个长度为\(n\)序列,每个点有个\(l\)值,会覆盖他前面\(l\)个点,问最后还剩多少个点没有被覆盖。(\(n\le 100000\))
题解
应该用差分简单做一下就行了,脑子一抽写了个线段树上去,幸亏过了2333(就当练手吧)
代码
const int N = 1e6 + 1e3;int sum[N << 2];#define lson o << 1, l, mid#define rson o << 1 | 1, mid + 1, r void Build(int o, int l, int r) { if (l == r) { sum[o] = 1; return ; } int mid = (l + r) >> 1; Build(lson); Build(rson); sum[o] = sum[o << 1] + sum[o << 1 | 1];}int ul, ur;int lazy[N << 2];void push_down(int o) { if (!lazy[o]) return; lazy[o << 1] = lazy[o << 1 | 1] = 1; sum[o << 1] = sum[o << 1 | 1] = 0; lazy[o] = 0; }void Update(int o, int l, int r) { if (ul <= l && r <= ur) { lazy[o] = 1; sum[o] = 0; return ;} push_down(o); int mid = (l + r) >> 1; if (ul <= mid) Update(lson); if (ur > mid) Update(rson); sum[o] = sum[o << 1] + sum[o << 1 | 1];}int main () { int n = read(); Build(1, 1, n); For (i, 1, n) { int len = read(); if (i == 1) continue ; if (!len) continue ; ul = max(i - len, 1); ur = i - 1; Update(1, 1, n); } printf ("%d\n", sum[1]); return 0;}
C. Pride
题意
给你一个长为\(n\)序列,你能进行一个操作,将相邻两个数中的一个替换为他们的\(gcd\),问至少进行多少次操作能把他们全部变成\(1\),如果不能完成输出\(-1\)。(\(n\le 2000\))
题解
这题一开始搞了我好久QAQ,想错了。其实就是先弄出一个\(1\),然后可以把其他所有的数字都变成\(1\),剩下的步数就是\(n-1\)。找那个步数,一定是一段连续区间\(gcd=1\)的情况,然后你用\(O(n^2)\)去枚举一下,找到一个可行的最小长度。还需要特判一开始就有\(1\)的情况,不然会挂掉(我就惨遭hack了TAT,后来又hack了两个可怜鬼233)。
代码
const int N = 2010;int a[N], n, ans = 0x7f7f7f7f, res, now;int main () { n = read(); res = n; For (i, 1, n) { a[i] = read(); if (a[i] == 1) -- res; now = __gcd(now, a[i]); } if (now != 1) return puts("-1"), 0; if (res != n) return printf ("%d\n", res), 0; For (i, 1, n) { int here = a[i], len = 0; if (here == 1) { ans = 0; continue ; } Fordown (j, i - 1, 1) { ++ len; here = __gcd(here, a[j]); if (here == 1) { chkmin(ans, len); break; } } } printf ("%d\n", ans + n - 1); return 0;}
D. Gluttony
题意
给你一个长为\(n\)序列,其中每个数字都不相同,让你输出一个原序列的一个排列,使得他们中任意一个子集(不能为空集或者全集)的和都互不相同(就是相同位置上的数的和互不相同就行了)(\(n\le 22\))
题解
一开始不知道做法,redbag大佬讲出来了正解,就是将每一个数换成后一个比它大的数,最大的数换成最小的那一个,就行了。
证明
这个还是比较好证明的。
考虑分两种情况讨论:
原子集没有最大的那个数:这就显然是个正确的,每个数都比之前的数要大,所以总和肯定要大;
原子集有最大的那个数:最大数改变后的值最整个里面最大的,然后你将这个子集去掉最大的那个数,剩下的变化量的和肯定要小于你最大值变为最小值的值,因为你之间肯定会有多余的空格(因为不能为全集)留着,所以肯定是可行的啦。
代码
int n, a[25];struct node { int val, pos; bool operator < (const node &rhs) const { return val < rhs.val; }};node lt[25];int ans[25];int main () { n = read(); For (i, 1, n) { lt[i].val = read(); lt[i].pos = i; } sort (lt + 1, lt + 1 + n); For (i, 1, n) if (i != n) ans[lt[i].pos] = lt[i + 1].val; else ans[lt[i].pos] = lt[1].val; For (i, 1, n) printf ("%d ", ans[i]); return 0;}
E. Envy
终于来填远古巨坑了。
题意
给你一个有 \(n\) 个点 \(m\) 条边的带边权无向连通图。
有 \(q\) 次询问,每次询问 \(k\) 条边是否能同时存在于 \(MST\) (最小生成树)中。(不要求在线回答)
\(n, m, q, \sum k \le 5 \times 10^5\)
题解
首先要知道一个很显然的结论。
就是在 \(Kruskal\) 求 \(MST\) 过程中,我们能否把一条边 \((u, v, w)\) 放入 \(MST\) 中,当且仅当 \(<w\) 的所有边联通后,\(u, v\) 仍然不连通。
所以对于每次询问来说,边权不相同的边是互不影响的,我们可以对于这些边权互不相同的边单独考虑。
相同 \(w\) 的如何考虑呢?首先 \(<w\) 的边全部加入,然后对于这些边按任意顺序加入的时候,不能存在 \((u, v)\) 相连的情况。
那么就可以得到一个离线算法了。把所有边权按权值排序,一条条加入。然后对于权值相同的同一询问的边,也逐个加入判断是否可行,最后利用可撤销并查集按秩合并的特性,把当前询问的边撤回就行了。
最后复杂度就是 \(O((m + \sum k) \log n)\) 的。(挂询问用了 std :: map, set
有点慢。。)
代码
还是比较好写的。。
#include#define For(i, l, r) for(register int i = (l), i##end = (int)(r); i <= i##end; ++i)#define Fordown(i, r, l) for(register int i = (r), i##end = (int)(l); i >= i##end; --i)#define Set(a, v) memset(a, v, sizeof(a))#define Cpy(a, b) memcpy(a, b, sizeof(a))#define debug(x) cout << #x << ": " << (x) << endl#define DEBUG(...) fprintf(stderr, __VA_ARGS__)#define fir first#define sec second#define mp make_pairusing namespace std;typedef pair PII;template inline bool chkmin(T &a, T b) { return b < a ? a = b, 1 : 0; }template inline bool chkmax(T &a, T b) { return b > a ? a = b, 1 : 0; }inline int read() { int x(0), sgn(1); char ch(getchar()); for (; !isdigit(ch); ch = getchar()) if (ch == '-') sgn = -1; for (; isdigit(ch); ch = getchar()) x = (x * 10) + (ch ^ 48); return x * sgn;}void File() {#ifdef zjp_shadow freopen ("E.in", "r", stdin); freopen ("E.out", "w", stdout);#endif}const int N = 5e5 + 1e3;struct Edge { int u, v, w;} lt[N];int Hash[N], tot;struct Data { int u, v, a, b;};namespace Union_Set { int fa[N], height[N]; int find(int x) { return x == fa[x] ? x : find(fa[x]); } Data stk[N]; int top; inline bool Merge(int u, int v, bool flag) { u = find(u); v = find(v); if (u == v) return false; if (flag) stk[++ top] = (Data) {u, v, height[u], height[v]}; if (height[u] > height[v]) swap(u, v); fa[u] = v; if (height[u] == height[v]) ++ height[v]; return true; } inline void Distract() { Data cur = stk[top --]; height[fa[cur.u] = cur.u] = cur.a; height[fa[cur.v] = cur.v] = cur.b; }}bool ans[N];map > M[N];inline int find_pos(int x) { return lower_bound(Hash + 1, Hash + tot + 1, x) - Hash;}int main () { File(); int n = read(), m = read(); using namespace Union_Set; For (i, 1, n) fa[i] = i; For (i, 1, m) { lt[i].u = read(); lt[i].v = read(); Hash[i] = lt[i].w = read(); } sort(Hash + 1, Hash + m + 1); tot = unique(Hash + 1, Hash + m + 1) - Hash - 1; int q = read(); For (i, 1, m) M[find_pos(lt[i].w)][q + 1].insert(mp(lt[i].u, lt[i].v)); For (i, 1, q) { int k = read(); ans[i] = true; For (j, 1, k) { int id = read(); M[find_pos(lt[id].w)][i].insert(mp(lt[id].u, lt[id].v)); } } For (i, 1, tot) { for (auto itm : M[i]) { if (itm.fir != q + 1 && !ans[itm.fir]) continue ; for (auto its : itm.sec) ans[itm.fir] &= Merge(its.fir, its.sec, itm.fir <= q); while (top) Distract(); } } For (i, 1, q) puts(ans[i] ? "YES" : "NO"); return 0;}