noip2024模拟赛记录
20241028
A.铁路 2
题意简述
给一棵树,每次跳一条简单路径,定义
求
思路
注意到,一定存在到直径端点的方案,找到直径,搜索即可
Code
code
const int N = 5e5 + 10; int n; vector<pair<int, ll>> g[N]; ll dep[N]; ll rt; ll pt; ll dis[N]; void dfs(int x, int fa) { if (dep[x] > dep[rt]) { rt = x; } for (auto [v, w] : g[x]) { if (v == fa) continue; dep[v] = dep[x] + w; dfs(v, x); } } void dfss(int x, int fa) { for (auto [v, w] : g[x]) { if (v == fa) continue; dep[v] = dep[x] + w; dfss(v, x); } } int solve() { dfs(1, 0); pt = rt; mms(dep, 0); dfs(pt, 0); mms(dep, 0); dfss(rt, 0); memcpy(dis, dep, sizeof(dep)); mms(dep, 0); dfss(pt, 0); rep(i, 1, n) chkmx(dis[i], dep[i]); sort(dis + 1, dis + n + 1); ll res = 0; debug(rt, pt); rep(i, 1, n) { res = 1ll * (1ll * res + 1ll * dis[i] % MOD * (n - i) % MOD) % MOD; debug(dis[i]); } // cout << res * 2 % MOD << endl; return 1ll * res * 2ll % MOD; } int travel(vector<int> U, vector<int> V, vector<int> W) { n = U.size() + 1; rep(i, 0, n - 2) { U[i]++; V[i]++; g[U[i]].push_back({V[i], W[i]}); g[V[i]].push_back({U[i], W[i]}); } return solve(); }
警察与小偷
https://www.luogu.com.cn/problem/P11237
分讨题,如果 ,那么一定在根追上,否则二分小偷能向上最大的距离,然后算一下在叶子最优,还是在路径上被追上最优
Code
struct Frac { int a, b; bool operator<(const Frac &other) const { return (__int128_t)a * other.b < (__int128_t)b * other.a; } bool operator<=(const Frac &other) const { return (__int128_t)a * other.b <= (__int128_t)b * other.a; } }; int n, q; vector<pii> g[N]; struct query { int p, v1, t, v2; } qry[N]; int dep[N], depw[N], rec[N][20]; int fawt[N]; int st[N], en[N], dfntot = 0; int mx[N], ex[N]; void dfs(int u, int fa) { st[u] = ++dfntot; rec[u][0] = fa; rep(i, 1, 19) rec[u][i] = rec[rec[u][i - 1]][i - 1]; for (auto [v, w] : g[u]) { if (v == fa) continue; depw[v] = depw[u] + w; fawt[v] = w; dep[v] = dep[u] + 1; dfs(v, u); chkmx(mx[u], mx[v] + w); } en[u] = dfntot; } void dfs2(int u, int fa) { int cur = ex[u] + fawt[u]; for (auto [v, w] : g[u]) { if (v == fa) continue; chkmx(ex[v], cur); chkmx(cur, mx[v] + w); } reverse(ALL(g[u])); cur = 0; for (auto [v, w] : g[u]) { if (v == fa) continue; chkmx(ex[v], cur); chkmx(cur, mx[v] + w); } for (auto [v, w] : g[u]) { if (v == fa) continue; dfs2(v, u); } } int jump(int u, int k) { per(i, 19, 0) { if (k & (1 << i)) u = rec[u][i]; } return u; } int getlca(int u, int v) { if (dep[u] < dep[v]) swap(u, v); per(i, 19, 0) if (dep[rec[u][i]] >= dep[v]) u = rec[u][i]; if (u == v) return u; per(i, 19, 0) { if (rec[u][i] != rec[v][i]) { u = rec[u][i]; v = rec[v][i]; } } return rec[u][0]; } int p, v1, t, v2, lca, len, lflen; int goid(int x) { if (x <= lflen) return jump(t, x); else return jump(p, len - x); } int getlf(int x, int v) { if (st[x] <= st[v] && en[x] >= st[v]) return ex[jump(v, dep[v] - dep[x] - 1)]; return mx[x]; } int check(int id) { if (id > len) return 0; int x = goid(id); int tdis = id <= lflen ? depw[t] - depw[x] : depw[x] + depw[t] - depw[lca] * 2; int pdis = id <= lflen ? depw[x] + depw[p] - depw[lca] * 2 : depw[p] - depw[x]; int dis = getlf(x, p); // debug(id, pdis, v1, tdis, v2); if ((Frac){ pdis, v1} <= (Frac){ tdis, v2}) return 0; return ((Frac){ tdis + dis, v2} < (Frac){ pdis + dis, v1}) ? 2 : 1; } int getdis(int x, int y) { return depw[x] + depw[y] - depw[getlca(x, y)] * 2; } vector<array<long long, 2>> police_thief(vector<signed> A, vector<signed> B, vector<signed> D, vector<signed> P, vector<signed> V1, vector<signed> T, vector<signed> V2) { n = A.size() + 1; q = P.size(); rep(i, 0, n - 2) { int u = A[i], v = B[i], w = D[i]; u++; v++; g[u].push_back({v, w}); g[v].push_back({u, w}); } rep(i, 0, q - 1) { int p, v1, t, v2; p = P[i] + 1, v1 = V1[i], t = T[i] + 1, v2 = V2[i]; qry[i + 1] = {p, v1, t, v2}; } dfs(1, 0); dfs2(1, 0); vector<array<long long, 2>> ans(q); rep(i, 1, q) { p = qry[i].p, v1 = qry[i].v1, t = qry[i].t, v2 = qry[i].v2; lca = getlca(p, t); debug(p, t, lca); len = dep[p] + dep[t] - 2 * dep[lca]; lflen = dep[t] - dep[lca]; Frac res = {0, 1}; if (check(0) < 2) { res = {depw[p] + depw[t] - depw[lca] * 2, (v1 - v2)}; } else { int l = 0, r = len; while (l + 1 < r) { int mid = (l + r) / 2; if (check(mid) == 2) l = mid; else r = mid; } int x = goid(l); res = {getdis(p, x) + getlf(x, p), v1}; debug(l, lflen, x, res.a, res.b); x = goid(l + 1); debug(x, res.a, res.b); if (check(l + 1)) res = max(res, {getdis(p, x) - getdis(t, x), v1 - v2}); } ans[i - 1] = {res.a, res.b}; } return ans; }
场上畏惧分讨而没有继续做,其实讨论的类不多,但是维护比较烦
20241029
岛屿
https://www.luogu.com.cn/problem/P11252
观察边数量的性质,一定出现一个连接 的边,找到,然后找一种染色方案后 bfs 确定其它的染色方案
跳跃游戏
https://www.luogu.com.cn/problem/P11239
等价于选择一些距离 的权值的和最大
考虑用线段树维护当前的 个,如何转移
- 直接保留原来的值
- 从前面转移过来,直接取前缀最大值,然后区间修改,全局max即可
Code
int n, q, k; signed rt = 1; struct segment_tree { struct node { signed ls, rs; int mx, lz; node() : ls(0), rs(0), mx(0), lz(0) {} void tag(int x) { mx += x; lz += x; } } tr[N << 6]; int tot = 1; void pushup(int p) { tr[p].mx = max(tr[tr[p].ls].mx, tr[tr[p].rs].mx); } void pushdown(int p) { if (tr[p].lz) { if (!tr[p].ls) tr[p].ls = ++tot; tr[tr[p].ls].tag(tr[p].lz); if (!tr[p].rs) tr[p].rs = ++tot; tr[tr[p].rs].tag(tr[p].lz); tr[p].lz = 0; } } void upd(signed &p, int l, int r, int ll, int rr, int val) { // debug(ll, rr); if (ll > rr) return; if (!p) p = ++tot; if (ll <= l && r <= rr) { // debug("flag", l, r, val); tr[p].tag(val); return; } pushdown(p); int mid = l + r >> 1; if (mid >= ll) upd(tr[p].ls, l, mid, ll, rr, val); if (mid < rr) upd(tr[p].rs, mid + 1, r, ll, rr, val); pushup(p); } void upd(signed &p, int l, int r, int pos, int val) { if (!p) p = ++tot; if (l == r) { chkmx(tr[p].mx, val); return; } pushdown(p); int mid = l + r >> 1; if (pos <= mid) upd(tr[p].ls, l, mid, pos, val); else upd(tr[p].rs, mid + 1, r, pos, val); pushup(p); } } seg; void upd(int pos, int val) { // debug(pos, pos / k, val); seg.tr[rt].tag((pos / k) * val); seg.upd(rt, 0, k - 1, 0, pos % k, val); } struct segt { int l, r; int val; }; vector<segt> vec; map<int, signed> mp; int sum[N]; pii rg[N]; int dp[N]; long long play_game(long long N, signed Q, long long K, vector<long long> L, vector<long long> R) { n = N; q = Q; k = K; // debug(n, q, k); mp[1] = 0; mp[n + 1] = 0; rep(i, 0, q - 1) { rg[i + 1] = {L[i] + 1, R[i] + 1}; mp[L[i] + 1]++; mp[R[i] + 2]--; } int pres = 0; for (auto it = mp.begin(); it != mp.end(); it++) { if ((*it).first == n + 1) break; pres += (*it).second; vec.push_back({(*it).first, (*next(it)).first - 1, pres}); } // for (auto [l, r, val] : vec) // debug(l, r, val); int cnt = 0; for (auto [l, r, val] : vec) { cnt++; upd(r, val); upd(l - 1, -val); dp[cnt] = seg.tr[rt].mx; // debug(cnt, dp[cnt]); if (dp[cnt] != dp[cnt - 1]) seg.upd(rt, 0, k - 1, (r + 1) % k, dp[cnt] - val); } return dp[cnt]; }
病毒
https://www.luogu.com.cn/problem/P11241
建点分树
在点分树
上刻画题目原来的条件
然后对于每个点 ,所有 子树内的 按照当前的深度建一个 表示深度为 的点,建 边
对于每个人向 建边,
然后跑 Dijkstra 即可
Code
int n, m, k; vector<int> g[N]; vector<pii> grp[N], val[N]; bool vis[N]; int cst[N]; int cnt = 0; void dfs(int x, int fa) { cnt++; for (auto v : g[x]) { if (v == fa || vis[v]) continue; dfs(v, x); } } int siz[N], mx[N], rt; void dfs2(int x, int fa) { siz[x] = 1; mx[x] = 0; for (auto v : g[x]) { if (v == fa || vis[v]) continue; dfs2(v, x); siz[x] += siz[v]; chkmx(mx[x], siz[v]); } chkmx(mx[x], cnt - siz[x]); if (mx[x] < mx[rt]) rt = x; } int pre[N], suf[N], tot; int cur = 0; void dfs3(int x, int fa, int dep) { if (dep > cur) { cur++; pre[cur] = ++tot; if (cur) grp[pre[cur]].push_back({pre[cur - 1], 0}); suf[cur] = ++tot; if (cur) grp[suf[cur - 1]].push_back({suf[cur], 0}); } grp[pre[dep]].push_back({m + x, cst[x]}); grp[m + x].push_back({suf[dep], 0}); for (auto v : g[x]) { if (v == fa || vis[v]) continue; dfs3(v, x, dep + 1); } } void dfs4(int x, int fa, int dep) { for (auto [d, id] : val[x]) { if (d >= dep) { grp[id].push_back({pre[min(cur, d - dep)], 0}); grp[suf[min(cur, d - dep)]].push_back({id, 0}); } } for (auto v : g[x]) { if (v == fa || vis[v]) continue; dfs4(v, x, dep + 1); } } void calc(int x) { cnt = rt = 0; mx[rt] = INF; dfs(x, 0); dfs2(x, 0); vis[rt] = true; cur = -1; dfs3(rt, 0, 0); dfs4(rt, 0, 0); for (auto v : g[rt]) { if (!vis[v]) calc(v); } } int dis[N]; void dijkstra(int s) { mms(dis, 0x3f); priority_queue<pii, vector<pii>, greater<pii>> q; q.push({0, s}); dis[s] = 0; while (!q.empty()) { auto [ds, u] = q.top(); q.pop(); if (ds > dis[u]) continue; for (auto [v, w] : grp[u]) { if (dis[u] + w < dis[v]) { dis[v] = dis[u] + w; q.push({dis[v], v}); } } } } vector<long long> find_spread(signed N, signed M, vector<signed> A, vector<signed> B, vector<signed> P, vector<signed> D, vector<signed> C) { n = N; m = M; mx[0] = INF; tot = n + m; rep(i, 0, n - 2) { g[A[i] + 1].push_back(B[i] + 1); g[B[i] + 1].push_back(A[i] + 1); } rep(i, 0, m - 1) { val[P[i] + 1].push_back({D[i], i + 1}); } rep(i, 1, n) cst[i] = C[i - 1]; calc(1); dijkstra(1); rep(i, 1, m) if (dis[i] >= INF) dis[i] = -1; return vector<long long>(dis + 1, dis + m + 1); }
20241101
美丽的序列
暴力 dp 即可
Code
void add(int &x, int y) { x += y; if (x >= MOD) x -= MOD; } int n, m; int mp[9][8][7][6][5][4][3][2], tmp[9][8][7][6][5][4][3][2]; int a[15]; void solve() { cin >> n >> m; rep(i, 1, m) cin >> a[i]; sort(a + 1, a + m + 1); reverse(a + 1, a + m + 1); mp[a[1]][a[2]][a[3]][a[4]][a[5]][a[6]][a[7]][a[8]] = 1; rep(i, 1, n) { mms(tmp, 0); rep(a1, 0, a[1]) { rep(a2, 0, a[2]) { rep(a3, 0, a[3]) { rep(a4, 0, a[4]) { rep(a5, 0, a[5]) { rep(a6, 0, a[6]) { rep(a7, 0, a[7]) { rep(a8, 0, a[8]) { int aa1 = a1 + (a1 < a[1]), aa2 = a2 + (a2 < a[2]), aa3 = a3 + (a3 < a[3]), aa4 = a4 + (a4 < a[4]), aa5 = a5 + (a5 < a[5]), aa6 = a6 + (a6 < a[6]), aa7 = a7 + (a7 < a[7]), aa8 = a8 + (a8 < a[8]); if (a[1] && aa1 == a[1]) { aa1 = 0; add(tmp[aa1][aa2][aa3][aa4][aa5][aa6][aa7][aa8], mp[a1][a2][a3][a4][a5][a6][a7][a8]); aa1 = a1 + (a1 < a[1]); } if (a[2] && aa2 == a[2]) { aa2 = 0; add(tmp[aa1][aa2][aa3][aa4][aa5][aa6][aa7][aa8], mp[a1][a2][a3][a4][a5][a6][a7][a8]); aa2 = a2 + (a2 < a[2]); } if (a[3] && aa3 == a[3]) { aa3 = 0; add(tmp[aa1][aa2][aa3][aa4][aa5][aa6][aa7][aa8], mp[a1][a2][a3][a4][a5][a6][a7][a8]); aa3 = a3 + (a3 < a[3]); } if (a[4] && aa4 == a[4]) { aa4 = 0; add(tmp[aa1][aa2][aa3][aa4][aa5][aa6][aa7][aa8], mp[a1][a2][a3][a4][a5][a6][a7][a8]); aa4 = a4 + (a4 < a[4]); } if (a[5] && aa5 == a[5]) { aa5 = 0; add(tmp[aa1][aa2][aa3][aa4][aa5][aa6][aa7][aa8], mp[a1][a2][a3][a4][a5][a6][a7][a8]); aa5 = a5 + (a5 < a[5]); } if (a[6] && aa6 == a[6]) { aa6 = 0; add(tmp[aa1][aa2][aa3][aa4][aa5][aa6][aa7][aa8], mp[a1][a2][a3][a4][a5][a6][a7][a8]); aa6 = a6 + (a6 < a[6]); } if (a[7] && aa7 == a[7]) { aa7 = 0; add(tmp[aa1][aa2][aa3][aa4][aa5][aa6][aa7][aa8], mp[a1][a2][a3][a4][a5][a6][a7][a8]); aa7 = a7 + (a7 < a[7]); } if (a[8] && aa8 == a[8]) { aa8 = 0; add(tmp[aa1][aa2][aa3][aa4][aa5][aa6][aa7][aa8], mp[a1][a2][a3][a4][a5][a6][a7][a8]); aa8 = a8 + (a8 < a[8]); } } } } } } } } } memcpy(mp, tmp, sizeof(mp)); } int res = 0; rep(a1, 0, a[1]) { rep(a2, 0, a[2]) { rep(a3, 0, a[3]) { rep(a4, 0, a[4]) { rep(a5, 0, a[5]) { rep(a6, 0, a[6]) { rep(a7, 0, a[7]) { rep(a8, 0, a[8]) { add(res, mp[a1][a2][a3][a4][a5][a6][a7][a8]); } } } } } } } } cout << res << endl; }
一
瞎搞做法,注意到如果不只有 add,sum 操作,序列实际要修改的数的个数是很少的,可以直接暴力
正确的做法是,用线段树维护,add,sum是容易的,or,xor,and 都可以等价与子树,合并反转操作,打tag记录即可
朋友
注意到每个鱼最后停在一个区间,令 表示左右端点在 内,包含 这个点的区间个数
考虑 表示区间 的答案,枚举这个区间中最大值的位置
Code
int n, m, w; int a[N]; int dp[405][405]; pii rg[405]; int s[405][405][405]; vector<int> uni; void solve() { cin >> n >> m >> w; rep(i, 1, n) cin >> a[i]; rep(i, 1, m) { int pos, val; cin >> pos >> val; int l = pos, r = pos; while (l > 1 && w - a[l] < val) l--; while (r < n && w - a[r] < val) r++; rg[i] = {l, r}; uni.push_back(l); uni.push_back(r); } sort(ALL(uni)); uni.erase(unique(ALL(uni)), uni.end()); int cnt = sz(uni); rep(i, 1, m) { rg[i].first = lower_bound(ALL(uni), rg[i].first) - uni.begin() + 1; rg[i].second = lower_bound(ALL(uni), rg[i].second) - uni.begin() + 1; debug(rg[i]); rep(j, rg[i].first, rg[i].second) s[rg[i].first][rg[i].second][j]++; } rep(k, 1, cnt) { per(i, cnt, 1) { rep(j, i + 1, cnt) { s[i][j][k] += s[i + 1][j][k] + s[i][j - 1][k] - s[i + 1][j - 1][k]; } } } rep(len, 1, cnt) { rep(l, 1, cnt - len + 1) { int r = l + len - 1; rep(x, l, r) { chkmx(dp[l][r], dp[l][x - 1] + dp[x + 1][r] + (s[l][r][x] * (s[l][r][x] - 1) / 2)); } } } cout << dp[1][cnt] << endl; }
20241105
沙漠点列
点双缩点,并计算非环上的边的个数,这样的边满足
然后贪心的先删除这样的边,再从大到小操作环
考场因为点双写成边双挂分,注意这2者容易混,且小数据下可能结果一样
Code
int n, m, k; int low[N], dfn[N], tot; stack<int> st; vector<int> g[N]; int cnt = 0; vector<int> vec; struct union_set { int fa[N]; void init(int n) { iota(fa + 1, fa + n + 1, 1); } int getfa(int x) { return fa[x] == x ? x : fa[x] = getfa(fa[x]); } void merge(int x, int y) { int fx = getfa(x), fy = getfa(y); if (fx != fy) fa[fx] = fy; } int count() { int res = 0; for (int i = 1; i <= n; i++) if (getfa(i) == i) res++; return res; } } uni; void tarjan(int u, int fa) { dfn[u] = low[u] = ++tot; st.push(u); for (auto v : g[u]) { if (v == fa) continue; if (!dfn[v]) { tarjan(v, u); chkmi(low[u], low[v]); if (low[v] > dfn[u]) cnt++; if (low[v] >= dfn[u]) { int tmp = 1; while (st.top() != v) { st.pop(); tmp++; } st.pop(); tmp++; vec.push_back(tmp); } } else { low[u] = min(low[u], dfn[v]); } } } void solve() { cin >> n >> m >> k; uni.init(n); rep(i, 1, m) { int u, v; cin >> u >> v; g[u].push_back(v); g[v].push_back(u); uni.merge(u, v); } rep(i, 1, n) if (!dfn[i]) tarjan(i, 0); int cur = uni.count(); if (k <= cnt) { cout << cur + k << endl; return; } cur += cnt; k -= cnt; sort(ALL(vec), greater<int>()); debug(cur, k, cnt, vec); for (auto v : vec) { if (k <= 1 || v == 2) break; if (k >= v) { k -= v; cur += v - 1; } else { k--; cur += k; k = 0; } } cout << cur << endl; }
集合划分
若一个集合 被选,所有 满足 为 子集, 的集合最多选 个,且 不属于其它 选择的集合
考虑 表示已经选择哪些数
Code
int n, m, k; int st[N]; bool vis[N]; int fr[N], id[N]; void solve() { cin >> n >> m >> k; rep(i, 1, m) { int x; cin >> x; st[x] = x; } rep(i, 1, (1 << n) - 1) { rep(j, 0, n - 1) { if (i & (1 << j)) st[i] |= st[i ^ (1 << j)]; } } vis[0] = true; rep(msk, 1, (1 << n) - 1) { int x = __builtin_popcount(msk); rep(i, 0, n - 1) { if (msk & (1 << i)) { if (vis[msk ^ (1 << i)] && ((k & (1 << (n - x))) || (!(st[((1 << n) - 1) ^ msk ^ (1 << i)] & (1 << i))))) { vis[msk] = true; fr[msk] = i; } } } } if (!vis[(1 << n) - 1]) { cout << "-1" << endl; return; } for (int i = (1 << n) - 1, j = 0; i; i = (i ^ (1 << fr[i])), j++) id[j] = fr[i]; rep(i, 1, (1 << n) - 1) { per(j, n - 1, 0) { if (i & (1 << id[j])) { cout << ((k >> j) & 1); break; } } } }
染色
转化为选出最多的不交区间,使得首尾颜色相同,维护是容易的,倍增即可
Code
int n, q; int st[N][20]; int la[N]; int l, r; int mx = 0; int x; int res; int t; void solve() { io.read(n); io.read(q); rep(i, 1, n) { io.read(x); chkmx(mx, la[x]); st[i][0] = mx; la[x] = i; } rep(i, 1, 19) { rep(j, (1 << i), n) { st[j][i] = st[st[j][i - 1]][i - 1]; } } while (q--) { io.read(l); io.read(r); if (l > r) swap(l, r); res = 0; t = (r - l + 1) * 2 - 2; per(i, __lg(r - l + 1), 0) { if (st[r][i] >= l) { r = st[r][i]; res |= (1 << i); } } io.write(t - res, '\n'); } }
20241108
C 山遥路远
考虑另一种判合法括号串的方式,即每次向左右拓展,直接spfa即可
遇到括号串,不只考虑前缀和>=0,还有左右交替拓展
Code
int n, m, cq; vector<pair<int, int>> g[N], rev[N]; int dp[N][N][2]; bool vis[N][N][2]; void solve() { cin >> n >> m >> cq; rep(i, 1, m) { int u, v, w, c; cin >> u >> v >> w >> c; if (c == 2) g[u].push_back({v, w}); else rev[v].push_back({u, w}); } priority_queue<pair<int, tuple<int, int, int>>, vector<pair<int, tuple<int, int, int>>>, greater<pair<int, tuple<int, int, int>>>> q; mms(dp, 0x3f); rep(i, 1, n) { dp[i][i][0] = 0; q.push({0, {i, i, 0}}); } while (!q.empty()) { auto [ds, tmp] = q.top(); auto [u, v, ty] = tmp; q.pop(); // vis[u][v][ty] = false; // debug(u, v, ty); if (dp[u][v][ty] != ds) continue; if (!ty) { rep(i, 1, n) { if (i != u && i != v && dp[u][v][0] + dp[v][i][0] < dp[u][i][0]) { dp[u][i][0] = dp[u][v][0] + dp[v][i][0]; q.push({dp[u][i][0], {u, i, 0}}); } if (i != u && i != v && dp[i][u][0] + dp[u][v][0] < dp[i][v][0]) { dp[i][v][0] = dp[i][u][0] + dp[u][v][0]; q.push({dp[i][v][0], {i, v, 0}}); } } for (auto [t, w] : rev[u]) { if (dp[t][v][1] > dp[u][v][ty] + w) { dp[t][v][1] = dp[u][v][ty] + w; q.push({dp[t][v][1], {t, v, 1}}); } } } else { for (auto [t, w] : g[v]) { if (dp[u][t][0] > dp[u][v][ty] + w) { dp[u][t][0] = dp[u][v][ty] + w; q.push({dp[u][t][0], {u, t, 0}}); } } } } while (cq--) { int u, v; cin >> u >> v; if (dp[u][v][0] >= INF) cout << "-1\n"; else cout << dp[u][v][0] % MOD << "\n"; } }
D 命运歧途
对于每种 的值,可以分开进行容斥
20241109
A前缀后缀
https://www.luogu.com.cn/problem/P9180
表示前缀到 时后缀的最大值,转移贪心即可
Code
int n, q; int a[N]; int mi[N][N]; int mx[N]; int la[N]; int tmp[N]; void solve() { cin >> n >> q; rep(i, 1, n) { cin >> a[i]; } rep(i, 1, n) { mx[i] = n; mi[i][i] = a[i]; rep(j, i + 1, n) { mi[i][j] = min(mi[i][j - 1], a[j]); } } int cnt = 0; while (q--) { int len, lim; cin >> len >> lim; vector<pii> pre, suf; rep(i, 1, n) tmp[i] = 0; rep(i, 1, n - len + 1) { if (mi[i][i + len - 1] >= lim) { pre.push_back({i, i + len - 1}); suf.push_back({-(i + len - 1), -i}); } } reverse(ALL(suf)); // debug(pre); rep(i, 1, n) { if (mx[i] <= 0) continue; auto it = lower_bound(ALL(suf), (pii){-mx[i], -INF}); if (it != suf.end()) chkmx(tmp[i], (-(*it).second) - 1); it = lower_bound(ALL(pre), (pii){i, 0}); if (it != pre.end()) chkmx(tmp[(*it).second + 1], mx[i]); } bool flag = false; bool flg = false; rep(i, 1, n) { la[i] = mx[i]; mx[i] = max(tmp[i], mx[i - 1]); if (mx[i] < i) mx[i] = 0; flag |= mx[i]; } if (!flag) { rep(i, 1, n) { if (la[i] - i + 1 == len && mi[i][i + len - 1] >= lim) { cnt++; break; } } cout << cnt << endl; return; } cnt++; } cout << cnt << endl; }
拉起窗帘
一定先用按钮,再手动,且两种操作的分界点关于 递减单调,所以直接 two-pointers 即可
Code
struct BIT { int cnt[N], sum[N]; void add(int x) { int val = x; x++; while (x) { cnt[x]++; sum[x] += val; x -= x & -x; } } pii query(int x) { x++; pii res = {0, 0}; while (x <= 2e5 + 5) { res.first += cnt[x]; res.second += sum[x]; x += x & -x; } return res; } } bit; int tot = 0; int n, t, s, k; int a[N]; int q; int tim[N], sum[N]; pii qry[N]; int res[N]; void init() { rep(i, 1, n) { bit.add(a[i]); tot += a[i]; } sort(a + 1, a + n + 1); int cur = 0; while (a[cur + 1] == 0) cur++; rep(i, 1, 2e5) { tim[i] = s + cur * k; sum[i] = sum[i - 1] + tim[i]; while (a[cur + 1] == i) cur++; } // debug(sum); } int calc(int x, int lim) { int res = sum[x]; pii lb = bit.query(x + lim); // debug(res); // res += (tot - lb.second - (n - lb.first) * (lim + x)) * t; // debug(res); // debug(lb); res += (lb.second - lb.first * (lim + x)) * t; return res; } void solve() { cin >> n >> t >> s >> k; rep(i, 1, n) { cin >> a[i]; } init(); cin >> q; rep(i, 1, q) { cin >> qry[i].first; qry[i].second = i; } sort(qry + 1, qry + q + 1); int cur = 0; // debug(calc(0, 0)); // return; per(i, q, 1) { auto [val, id] = qry[i]; while (calc(cur + 1, val) <= calc(cur, val)) cur++; res[id] = calc(cur, val); // cout << cur << ' ' << val << ' ' << res[id] << endl; } rep(i, 1, q) cout << res[i] << ' '; }
直径
考虑所有二叉和三叉树,打表发现覆盖了所有情况
注意corner case!!!
本文作者:xiaruize's Blog
本文链接:https://www.cnblogs.com/xiaruize/p/18575226
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理