数据结构
自古不会数据结构,只会瞎写。/kk
CF1039D You Are Given a Tree
题目大意
给定一棵树,对于 /(k /in [1,n]/) 求出,树中最多有多少不相交的路径,每条路径恰好包含 /(k/) 个点。
题解
首先假设只有一个给定的 /(k/) ,那么可以 /(O(n)/) 求出答案,然而要对 /(n/) 个 /(k/) 求解, /(O(n^2)/) 显然不行。不难发现,当 /(k > /sqrt(n)/) 时,答案一定不会超过 /(/sqrt(n)/) ,因此对于 /(k > /sqrt(n)/) 时的 /(O(n)/) 种情况,一共只有 /(O(/sqrt(n))/) 种可能的答案,于是可以相对于前 /(/sqrt(n)/) 暴力 /(O(n)/) 求出答案,大于 /(/sqrt(n)/) 的部分对于每个答案进行二分,求哪些 /(k/) 的答案均相同,时间复杂度 /(O(n /sqrt(n) /log n)/)。
直接做需要奇怪的卡常科技,不知道为什么整体二分会快这么多。。而且好写。。不会分析复杂度。。
code
#include <bits/stdc++.h> const int N = 1e5 + 10; int read(void){ int f = 1, x = 0; char ch = getchar(); while(!isdigit(ch)) { if(ch == '-') f = -1; ch = getchar(); } while(isdigit(ch)) { x = x * 10 + ch - 48; ch = getchar(); } return f * x; } int n, ans[N], f[N]; int head[N], nxt[N << 1], to[N << 1], cnt; void add(int u, int v) { nxt[++cnt] = head[u], to[cnt] = v, head[u] = cnt; } int dfn[N], num[N], fa[N], ind; void dfs(int u, int f){ dfn[u] = ++ind, fa[num[ind] = u] = f; for(int i = head[u]; i; i = nxt[i]){ int v = to[i]; if(v ^ f) dfs(v, u); } } int calc(int x){ int res = 0; for(int i = n; i; --i){ int u = num[i], a = 0, b = 0; for(int i = head[u]; i; i = nxt[i]){ int v = to[i]; if(v == fa[u]) continue; if(f[v] >= a) b = a, a = f[v]; else if(f[v] > b) b = f[v]; } if(a + b + 1 >= x) ++res, f[u] = 0; else f[u] = a + 1; } return res; } void solve(int l, int r, int L, int R){ if(l > r || L > R) return; if(l == r){ for(int i = L; i <= R; ++i) ans[i] = l; return; } int mid = (L + R) >> 1; ans[mid] = calc(mid); solve(ans[mid], r, L, mid - 1); solve(l, ans[mid], mid + 1, R); } signed main(void){ n = read(); for(int i = 1, x, y; i < n; ++i){ x = read(), y = read(); add(x, y), add(y, x); } dfs(1, 0); ans[1] = n; solve(0, n / 2, 2, n); for(int i = 1; i <= n; ++i) printf("%d/n", ans[i]); return 0; }
CF983E NN country
题目大意
有一棵树,树上有若干条路径,每条路径 /(x, y/) 包含路径上所有的点,有 /(q/) 次询问,每次询问 /(u,v/) 表示从 /(u/) 走到 /(v/) 至少经过多少条路径。
题解
首先不难求出对于每个点经过一条路径所能到达的最靠上的点在哪里,于是可以考虑对其进行倍增,每次询问 /((u, v)/) 时,直接倍增到 /(lca/) 下方,恰好下一次能到达 /(lca/) 的地方,此时问题变为是否存在一条路径包含路径 /((u, v)/) ,使得 /(u/) 恰好一次能到 /(v/) ,这本质上是一个二维偏序问题,即是否存在一条路径一个端点在 /(u/) 子树内,另一个端点在 /(v/) 子树内,通过主席树解决即可。
没啥细节就不放代码了。。。
CF1422F Boring Queries
题目大意
给定一个长度为 /(n/) 的序列 /(a/),有 /(q/) 次询问,每次给定 /(l, r/) ,求这个区间的 /(lcm/) 。
题解
考虑 /(lcm/) 的本质是什么,对于一些数形如 /(x = p_1^{c_1}p_2^{c_2}…p_k^{c_k}/) ,取 /(lcm/) 的本质就是对所有的 /(c_i/) 取 /(/max/) ,考虑从左到右枚举 /(r/) 回答,不难发现,若 /(i /le r/) ,存在一个质因子在 /(a_r/) 中的次数大于在 /(a_i/) 中的次数,那么 /(a_i/) 就没用了,这类似一个单调栈,于是可以对每个质数单独维护一个单调栈,在用线段树维护每个位置的答案即可,由于题目强制在线,所以将线段树可持久化一下就行了。。
code
#include <bits/stdc++.h> #define fi first #define se second const int N = 2e5 + 10, mod = 1e9 + 7; int read(void) { int f = 1, x = 0; char ch = getchar(); while(!isdigit(ch)) { if(ch == '-') f = -1; ch = getchar(); } while(isdigit(ch)) { x = x * 10 + ch - 48; ch = getchar(); } return f * x; } using std::pair; using std::make_pair; int inv[N], pri[N], cnt; bool vis[N]; std::unordered_map<int, int> mp[N]; void pre(void) { inv[0] = inv[1] = 1; for(int i = 2; i < N; ++i) { if(!vis[i]) pri[++cnt] = i; for(int j = 1; j <= cnt && pri[j] * i < N; ++j) { vis[i * pri[j]] = 1; if(!(i % pri[j])) break; } inv[i] = 1ll * (mod - mod / i) * inv[mod % i] % mod; } for(int i = 1; i <= cnt; ++i) for(int j = 1; j * pri[i] < N; ++j) mp[j * pri[i]][i] = j % pri[i] ? pri[i] : mp[j][i] * pri[i]; } struct range { int l, r, w; }; std::vector<range> pos[N]; int n, q, a[N], rt[N], tot; struct Tree { int ls, rs, mul; } t[N << 7]; void add(int& p, int lst, int l, int r, int w, int L = 1, int R = n) { t[p = ++tot] = t[lst]; int mid = (L + R) >> 1; if(l <= L && r >= R) { return void(t[p].mul = 1ll * t[p].mul * w % mod); } if(l <= mid) add(t[p].ls, t[lst].ls, l, r, w, L, mid); if(r > mid) add(t[p].rs, t[lst].rs, l, r, w, mid + 1, R); } int que(int p, int x, int L = 1, int R = n) { if(L == R) return t[p].mul; int mid = (L + R) >> 1; if(x <= mid) return 1ll * t[p].mul * que(t[p].ls, x, L, mid) % mod; else return 1ll * t[p].mul * que(t[p].rs, x, mid + 1, R) % mod; } signed main(void) { n = read(), t[0].mul = 1, pre(); for(int i = 1; i <= n; ++i) a[i] = read(); for(int i = 1; i <= n; ++i) { rt[i] = rt[i - 1]; for(pair<int, int> x : mp[a[i]]) { while(!pos[x.fi].empty() && pos[x.fi].back().w <= x.se) { range ul = pos[x.fi].back(); pos[x.fi].pop_back(); add(rt[i], rt[i], ul.l, ul.r, inv[ul.w]); } int p = pos[x.fi].empty() ? 1 : pos[x.fi].back().r + 1; pos[x.fi].push_back( { p, i, x.se } ); add(rt[i], rt[i], p, i, x.se); } } q = read(); for(int i = 1, l, r, lst = 0; i <= q; ++i) { l = (read() + lst) % n + 1, r = (read() + lst) % n + 1; if(l > r) l ^= r, r ^= l, l ^= r; printf("%d/n", lst = que(rt[r], l)); } return 0; }
CF503E Pastoral Oddities
题目大意
给定一张 /(n/) 个点 /(m/) 条边的图,初始时没有边,每次往里加一条边,边有边权,求是否存在一个边集满足每个点的度数均为奇数,若存在,输出边集中最大边权的最小值。
题解
乍一看像是神仙题,果然是神仙题。。
有一个很强的结论,每个点的度数为奇数的充要条件为所有联通块的大小均为偶数。
必要性证明:考虑反证法,大小为奇数的连通块内所有点的度数为奇数,则一定有连通块内度数和为奇数,而一个无向图连通块度数和一定为偶数,相悖。
充分性证明:考虑有一个偶数大小的连通块,我们一定可以通过以下方法构造出一种合法解,从叶子开始向上走,一个点若有奇数个儿子,就删掉它和父亲之间的边,最终除了根节点和被断开的节点,所有点都有偶数个儿子。
这样,我们可以考虑维护这张图的最小生成森林,维护每棵树的大小,同时在 /(link/) 和 /(cut/) 时维护奇连通块的数量,这里可以用 /(LCT/) 维护子树信息实现。考虑每次新加入一条边奇连通块数量肯定不会变多,因此每一条新边能加入就加入,此时有一些边可能对奇联通块数没有影响,删除最大的对奇连通块数没有影响的边,可以用一个堆来维护森林中的所有边,不能删除的最大的边便是答案。
code
//How I wish, how I wish you were here. #include <bits/stdc++.h> #define ll long long #define fi first #define se second const int N = 5e5 + 10; int read(void) { int f = 1, x = 0; char ch = getchar(); while(!isdigit(ch)) { if(ch == '-') f = -1; ch = getchar(); } while(isdigit(ch)) { x = x * 10 + ch - 48; ch = getchar(); } return f * x; } using std::pair; using std::make_pair; int n, m, u[N], v[N], w[N]; bool Cut[N]; std::priority_queue<pair<int, int> > q; struct Link_Cut_Tree { int fa[N], siz[N], son[N][2], inv[N], stk[N], top, cnt; std::pair<int, int> mx[N], a[N]; bool lz[N]; bool right(int p) { return son[fa[p]][1] == p; } bool nroot(int p) { return son[fa[p]][0] == p || son[fa[p]][1] == p; } void update(int p) { mx[p] = std::max(std::max(mx[son[p][0]], mx[son[p][1]]), a[p]); siz[p] = inv[p] + siz[son[p][0]] + siz[son[p][1]] + (p <= n); } void pushrev(int p) { lz[p] ^= 1, std::swap(son[p][0], son[p][1]); } void pushdown(int p) { if(!lz[p]) return; lz[p] = 0; if(son[p][0]) pushrev(son[p][0]); if(son[p][1]) pushrev(son[p][1]); } void rotate(int p) { int f1 = fa[p], f2 = fa[f1], id = right(p), w = son[p][id ^ 1]; if(nroot(f1)) son[f2][right(f1)] = p; son[p][id ^ 1] = f1, son[f1][id] = w; if(w) fa[w] = f1; fa[f1] = p, fa[p] = f2; update(f1); } void splay(int p) { int u = p; stk[top = 1] = u; while(nroot(u)) stk[++top] = u = fa[u]; while(top) pushdown(stk[top--]); while(nroot(p)) { if(nroot(fa[p])) rotate(right(p) == right(fa[p]) ? fa[p] : p); rotate(p); } update(p); } void access(int p) { for(int x = 0; p; p = fa[x = p]) { splay(p), inv[p] += siz[son[p][1]]; inv[p] -= siz[son[p][1] = x], update(p); } } void makert(int p) { access(p), splay(p), pushrev(p); } int findrt(int p) { access(p), splay(p), pushdown(p); while(son[p][0]) pushdown(p = son[p][0]); return splay(p), p; } void link(int u, int v) { makert(u), access(v), splay(v); cnt -= siz[u] & 1, cnt -= siz[v] & 1; fa[u] = v, inv[v] += siz[u], update(v); cnt += siz[v] & 1; } void cut(int u, int v) { makert(u), access(v), splay(v), cnt -= siz[v] & 1; son[v][0] = fa[u] = 0, update(v); cnt += siz[u] & 1, cnt += siz[v] & 1; } int add(int id) { int u = ::u[id], v = ::v[id], w = ::w[id]; bool flag = 1; if(findrt(u) == findrt(v)) { makert(u), access(v), splay(v); int ul = mx[v].se; if(w < mx[v].fi) cut(ul, ::u[ul - n]), cut(ul, ::v[ul - n]), Cut[ul - n] = 1; else flag = 0; } if(flag) { a[id + n] = mx[id + n] = make_pair(w, id + n); link(id + n, u), link(id + n, v), q.push(make_pair(w, id)); } if(cnt) return -1; while(!q.empty()) { int id = q.top().se; q.pop(); if(Cut[id]) continue; cut(id + n, ::u[id]), cut(id + n, ::v[id]); if(cnt) { link(id + n, ::u[id]), link(id + n, ::v[id]); q.push(make_pair(::w[id], id)); return ::w[id]; } } } } t; signed main(void) { t.cnt = n = read(), m = read(); for(int i = 1; i <= n; ++i) t.siz[i] = 1; for(int i = 1; i <= m; ++i) { u[i] = read(), v[i] = read(), w[i] = read(); printf("%d/n", t.add(i)); } return 0; }
605菜市场
改编自模拟赛的水题,太水所以没有题解,欢迎大家爆切。
code
#include <bits/stdc++.h> #define int long long const int N = 1e5 + 10; int read(void) { int f = 1, x = 0; char ch = getchar(); while(!isdigit(ch)) { if(ch == '-') f = -1; ch = getchar(); } while(isdigit(ch)) { x = x * 10 + ch - 48; ch = getchar(); } return f * x; } int n, q, g, r, op, mod; int tot, rt[N], d[N], f[N], stp[N][20]; struct TRE { int ls, rs, mn; } t[N << 7]; void cover(int &p, int lst, int l, int r, int w, int L = 0, int R = mod - 1) { t[p = ++tot] = t[lst]; if(!lst) t[p].mn = n; if(l <= L && r >= R) return void(t[p].mn = w); int mid = (L + R) >> 1; if(l <= mid) cover(t[p].ls, t[lst].ls, l, r, w, L, mid); if(r > mid) cover(t[p].rs, t[lst].rs, l, r, w, mid + 1, R); } int que(int p, int x, int L = 0, int R = mod - 1) { if(!p) return n; if(L == R) return t[p].mn; int mid = (L + R) >> 1; if(x <= mid) return std::min(t[p].mn, que(t[p].ls, x, L, mid)); else return std::min(t[p].mn, que(t[p].rs, x, mid + 1, R)); } signed main(void) { n = read(), q = read(), g = read(), r = read(), op = read(), mod = r + g; for(int i = 1; i <= n; ++i) d[i] = d[i - 1] + read(); for(int i = n - 1, p, L, R; i; --i) { stp[i][0] = p = que(rt[i + 1], d[i] % mod); f[i] = f[p] + d[p] - d[i]; if(p ^ n) f[i] += mod - (d[p] - d[i]) % mod; for(int j = 1; j < 20; ++j) stp[i][j] = stp[stp[i][j - 1]][j - 1]; R = (d[i] - g + mod) % mod, L = (d[i] - g - r + 1 + mod) % mod; if(L <= R) cover(rt[i], rt[i + 1], L, R, i); else cover(rt[i], rt[i + 1], L, mod - 1, i), cover(rt[i], rt[i], 0, R, i); } for(int i = 1, t, l, r, res = 0, p; i <= q; ++i) { t = read(), l = (read() ^ (op * res)) % (n + 1), r = (read() ^ (op * res)) % (n + 1); if(l > r) l ^= r, r ^= l, l ^= r; p = que(rt[l + 1], (mod + d[l] % mod - t % mod) % mod); if(p >= r) { printf("%lld/n", res = d[r] - d[l] + t); continue; } res = d[p] - d[l] + t + mod - (d[p] - d[l] + t) % mod + f[p]; for(int j = 19; ~j; --j) if(stp[p][j] && stp[p][j] < r) p = stp[p][j]; res += d[r] - d[p] - f[p]; printf("%lld/n", res); } return 0; }