#include<cstdio> using ll = longlong; constint N = 2e5 + 8;
inline ll read(){ ll Y = getchar(), S = 0, C = 1; while (Y > 57 || Y < 48) { if (Y == 45) C = -1; Y = getchar(); } while (Y <= 57 && Y >= 48) { S = (S << 3) + (S << 1) + Y - 48; Y = getchar(); } return S * C; }
structSEGTREE { int siz; ll sum[4]; } tree[N << 2]; int L = -1e5, R = 1e5; // 指定递归起点 int n, m; ll a[N], b[N], sum0; #define ls p<<1 #define rs p<<1|1 #define mid (cl+cr>>1)
#include<cstdio> using ll = longlong; constint N = 16000000 + 8;
inline ll read(){ ll Y = getchar(), S = 0, C = 1; while (Y > 57 || Y < 48) { if (Y == 45) C = -1; Y = getchar(); } while (Y <= 57 && Y >= 48) { S = (S << 3) + (S << 1) + Y - 48; Y = getchar(); } return S * C; }
structSEGTREE { int val, mrk = -1; int ls, rs; } tree[N]; int L, R, pcnt; #define mid (cl+cr>>1)
// 在这两个函数中写需要维护的东西 inlinevoidpush_up(SEGTREE& p, SEGTREE l, SEGTREE r){ p.val = l.val + r.val; } inlinevoidpush_down(int& p, int x, int len){ if (!p) p = ++pcnt; // 开点 tree[p].val = x * len; tree[p].mrk = x; }
// 下传与回溯 inlinevoidpush_up(int p){ push_up(tree[p], tree[tree[p].ls], tree[tree[p].rs]); } inlinevoidpush_down(int p, int len){ if (len <= 1 || tree[p].mrk == -1) return; // 这里没有标记 返回 push_down(tree[p].ls, tree[p].mrk, len - len / 2); // 左子区间传递 push_down(tree[p].rs, tree[p].mrk, len / 2); // 右子区间传递 tree[p].mrk = -1; // 传递后 标记清空 }
SEGTREE query(int l, int r, int p = 1, int cl = L, int cr = R){ if (cl >= l && cr <= r) return tree[p]; // 查询的区间包含当前节点区间 返回当前节点区间 push_down(p, cr - cl + 1); if (mid >= r) returnquery(l, r, tree[p].ls, cl, mid); if (mid < l) returnquery(l, r, tree[p].rs, mid + 1, cr); SEGTREE ans; push_up(ans, query(l, r, tree[p].rs, mid + 1, cr), query(l, r, tree[p].ls, cl, mid)); return ans; }
voidupdate(int l, int r, int d, int p = 1, int cl = L, int cr = R){ if (cl >= l && cr <= r) returnpush_down(p, d, cr - cl + 1); push_down(p, cr - cl + 1); if (mid >= l) update(l, r, d, tree[p].ls, cl, mid); if (mid < r) update(l, r, d, tree[p].rs, mid + 1, cr); push_up(p); }
intmain(){ int n = read(); L = 1, R = n, pcnt = 0; // 初始化数据范围[L,R] update(1, n, 1); for (int q = read(); q--; ) { int l = read(), r = read(), op = read(); update(l, r, op - 1); printf("%d\n", query(1, n).val); // 等价于 tree[1].val } return0; }
inline ll read(){ ll Y = getchar(), S = 0, C = 1; while (Y > 57 || Y < 48) { if (Y == 45) C = -1; Y = getchar(); } while (Y <= 57 && Y >= 48) { S = (S << 3) + (S << 1) + Y - 48; Y = getchar(); } return S * C; }
constint N = 16000000 + 8; structSEGTREE { int val, ls, rs; } tree[N]; int L, R, rt, pcnt; // 递归起点 根节点 节点时间戳
voidupdate(int x, int d, int& p = rt, int cl = L, int cr = R){ if (!p) p = ++pcnt; if (cl == cr) return tree[p].val += d, void(); int mid = (cl + cr) >> 1; if (x <= mid) update(x, d, tree[p].ls, cl, mid); if (x > mid) update(x, d, tree[p].rs, mid + 1, cr); tree[p].val = tree[tree[p].ls].val + tree[tree[p].rs].val; }
intquery(int l, int r, int p = 1, int cl = L, int cr = R){ if (cl >= l && cr <= r) return tree[p].val; int mid = (cl + cr) >> 1; if (mid >= r) returnquery(l, r, tree[p].ls, cl, mid); if (mid < l) returnquery(l, r, tree[p].rs, mid + 1, cr); returnquery(l, r, tree[p].rs, mid + 1, cr) + query(l, r, tree[p].ls, cl, mid); }
#include<cstdio> #include<algorithm> #include<cmath> using ll = longlong; constint N = 4e5 + 8, M = 1e7;
inline ll read(){ ll Y = getchar(), S = 0, C = 1; while (Y > 57 || Y < 48) { if (Y == 45) C = -1; Y = getchar(); } while (Y <= 57 && Y >= 48) { S = (S << 3) + (S << 1) + Y - 48; Y = getchar(); } return S * C; }
/// ----------------线段树---------------- structSEGTREE { int siz, mrk; double mul; int ls, rs; } tree[M]; int n, L, R, pcnt, rt[N]; #define mid (cl+cr>>1)
std::vector<int> E[N]; int Log2[N], fa[33][N], dep[N], w[N]; int rt[N];
voidinitlca(int u, int fau = 0){ dep[u] = dep[fau] + 1; fa[0][u] = fau; for (int i = 1; i <= Log2[dep[u]]; ++i) { fa[i][u] = fa[i-1][fa[i-1][u]]; } // u的第2^{i-1}个父节点的第2^{i-1}个父节点即第2^{i}个父节点 for (auto v : E[u]) { initlca(v, u); } }
// v的k级祖先 intgetast(int v, int k){ k = dep[v] - k; // 期望的深度 if (k <= 0) return-1; // v没有k级祖先 while (dep[v] > k) v = fa[Log2[dep[v] - k]][v]; return v; }
离线存储答案:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
std::vector<std::pair<int, int> > que[N]; int ans[N];
int q = read(); for (int i = 1; i <= q; ++i) { int v = read(), p = read(); int fav = getast(v, p); // v的p级祖先 que[fav].push_back({ dep[v],i }); // 存储每个询问 第二维是编号id }
/* 计算答案 */
for (int i = 1; i <= q; ++i) { printf("%d ", ans[i]); }
#include<cstdio> #include<vector> using ll = longlong; constint N = 2e5 + 8, M = 1e7 + 8;
inline ll read(){ ll Y = getchar(), S = 0, C = 1; while (Y > 57 || Y < 48) { if (Y == 45) C = -1; Y = getchar(); } while (Y <= 57 && Y >= 48) { S = (S << 3) + (S << 1) + Y - 48; Y = getchar(); } return S * C; }
/// ----------------主函数---------------- intmain(){ int n = read(); L = 0, R = n + 1, pcnt = 0; for (int i = 2; i < N; ++i) Log2[i] = Log2[i / 2] + 1; for (int i = 1; i <= n; ++i) { int x = read(); if (!x) x = n + 1; // 给森林一个虚拟根节点n+1 E[x].push_back(i); } initlca(n + 1); int q = read(); for (int i = 1; i <= q; ++i) { int v = read(), p = read(); int fav = getast(v, p); // v的p级祖先 if (fav == -1) ans[i] = 0; // v没有p级祖先 答案是0 else que[fav].push_back({ dep[v],i }); // 存储每个询问 }
dfs(n + 1); // 线段树合并 计算答案
for (int i = 1; i <= q; ++i) { printf("%d ", ans[i]); }
#include<cstdio> #include<algorithm> using ll = longlong; constint N = 5e5 + 10;
inline ll read(){ ll Y = getchar(), S = 0, C = 1; while (Y > 57 || Y < 48) { if (Y == 45) C = -1; Y = getchar(); } while (Y <= 57 && Y >= 48) { S = (S << 3) + (S << 1) + Y - 48; Y = getchar(); } return S * C; }
voidupdate(int l, int r, int d, int p = 1, int cl = 1, int cr = n){ if (cl >= l && cr <= r) { if (tree[p].mn >= d) return; if (tree[p].mx < d) returnpush_down(tree[p], d, cr - cl + 1); } ... }
#include<cstdio> #include<algorithm> using ll = longlong; constint N = 2e5 + 10;
inline ll read(){ ll Y = getchar(), S = 0, C = 1; while (Y > 57 || Y < 48) { if (Y == 45) C = -1; Y = getchar(); } while (Y <= 57 && Y >= 48) { S = (S << 3) + (S << 1) + Y - 48; Y = getchar(); } return S * C; }
/// ----------------线段树---------------- structSEGMENT { ll val; // 区间和 int mn, mx; // 区间最值 ll mrk; // 懒标记 } tree[N << 2]; int arr[N]; int n, m; #define ls p<<1 #define rs p<<1|1 #define mid (cl+cr>>1)
// 下传与回溯 voidpush_up(int p){ push_up(tree[p], tree[ls], tree[rs]); } voidpush_down(int p, int len){ if (!tree[p].mrk) return; push_down(tree[ls], tree[p].mrk, len - len / 2); push_down(tree[rs], tree[p].mrk, len / 2); tree[p].mrk = 0; }
// 建树 当前节点区间[cl,cr] 节点编号p voidbuild(int p = 1, int cl = 1, int cr = n){ if (cl == cr) return tree[p] = { arr[cl], arr[cl], arr[cl], 0 }, void(); build(ls, cl, mid); build(rs, mid + 1, cr); push_up(p); }
// 查询[l,r] 当前节点区间[cl,cr] 节点编号p voidupdate(int l, int r, int d, int p = 1, int cl = 1, int cr = n){ if (cl >= l && cr <= r) { if (tree[p].mn >= d) return; if (tree[p].mx < d) returnpush_down(tree[p], d, cr - cl + 1); } push_down(p, cr - cl + 1); if (mid >= l) update(l, r, d, ls, cl, mid); if (mid < r) update(l, r, d, rs, mid + 1, cr); push_up(p); }
// 二分查询[l,r] 当前剩下的钱cur 当前节点区间[cl,cr] 节点编号p intbisrch(int l, int r, int& cur, int p = 1, int cl = 1, int cr = n){ if (cl >= l && cr <= r) { if (cur < tree[p].mn) return0; // 买不起 if (cur >= tree[p].val) return cur -= tree[p].val, cr - cl + 1; // 买了cr-cl+1个,花费tree[p].val if (cl == cr) return0; // 递归到叶 } push_down(p, cr - cl + 1); int ans = 0; if (mid >= l) ans += bisrch(l, r, cur, ls, cl, mid); if (mid < r) ans += bisrch(l, r, cur, rs, mid + 1, cr); return ans; }
/// ----------------主函数---------------- intmain(){ n = read(), m = read(); for (int i = 1; i <= n; ++i) arr[i] = read(); build(); while (m--) { int op = read(), x = read(), y = read(); if (op == 1) update(1, x, y); elseprintf("%d\n", bisrch(x, n, y)); } return0; }
#include<cstdio> #include<iostream> #include<algorithm> #include<vector> using ll = longlong; constint N = 4e5 + 10;
inline ll read(){ ll Y = getchar(), S = 0, C = 1; while (Y > 57 || Y < 48) { if (Y == 45) C = -1; Y = getchar(); } while (Y <= 57 && Y >= 48) { S = (S << 3) + (S << 1) + Y - 48; Y = getchar(); } return S * C; }
SEGTREE query(int l, int r, int p = 1, int cl = 1, int cr = n){ if (cl >= l && cr <= r) return tree[p]; // 查询的区间包含当前节点区间 返回当前节点区间 push_down(p); if (mid >= r) returnquery(l, r, ls, cl, mid); if (mid < l) returnquery(l, r, rs, mid + 1, cr); SEGTREE ans; push_up(ans, query(l, r, ls, cl, mid), query(l, r, rs, mid + 1, cr)); return ans; }
voidupdate(int l, int r, ll d, int p = 1, int cl = 1, int cr = n){ if (cl >= l && cr <= r) returnpush_down(tree[p], d); // 查询的区间包含当前节点区间 push_down(p); if (mid >= l) update(l, r, d, ls, cl, mid); if (mid < r) update(l, r, d, rs, mid + 1, cr); push_up(p); }
/// ----------------DFS序---------------- std::vector<int> E[N]; int unix, dfsn[N], dfsnR[N]; // DFS树: 时间戳 DFS序 子树的最大DFS序 voiddfs(int u, int fau = 0){ dfsn[u] = ++unix; for (auto v : E[u]) { if (v != fau) dfs(v, u); } dfsnR[u] = unix; }
/// ----------------主函数---------------- inlineintcount1s(ll x){ int ans = 0; for (int bit = 0; bit < 64; ++bit) if ((1ll << bit) & x) ++ans; return ans; } ll w[N]; signedmain(){ n = read(); int q = read(); for (int i = 1; i <= n; ++i) { w[i] = read(); } for (int i = 2; i <= n; ++i) { int u = read(), v = read(); E[u].push_back(v); E[v].push_back(u); } dfs(1);
for (int i = 1; i <= n; ++i) { arr[dfsn[i]] = 1ll << w[i]; } build();
while (q--) { int op = read(); if (op == 1) { ll x = read(), d = 1ll << read(); update(dfsn[x], dfsnR[x], d); } else { int x = read(); ll res = query(dfsn[x], dfsnR[x]).val; printf("%d\n", count1s(res)); } }
#include<cstdio> #include<vector> #include<algorithm> using ll = longlong; constint N = 2e5 + 8, M = 1e7 + 8;
inline ll read(){ ll Y = getchar(), S = 0, C = 1; while (Y > 57 || Y < 48) { if (Y == 45) C = -1; Y = getchar(); } while (Y <= 57 && Y >= 48) { S = (S << 3) + (S << 1) + Y - 48; Y = getchar(); } return S * C; }
/// ----------------线段树---------------- structSEGTREE { int cnt; // 叶节点的某种颜色个数 int val; // 颜色种类 int ls, rs; } tree[M]; int n, pcnt; #define mid (cl+cr>>1)
voidupdate(int x, int d, int& p, int cl = 1, int cr = n){ if (!p) p = ++pcnt; if (cl == cr) returnpush_down(p, d); if (x <= mid) update(x, d, tree[p].ls, cl, mid); if (x > mid) update(x, d, tree[p].rs, mid + 1, cr); push_up(p); }
intmerge(int x, int y, int cl = 1, int cr = n){ if (!x || !y) return x | y; if (cl == cr) returnpush_down(x, tree[y].cnt), x; tree[x].ls = merge(tree[x].ls, tree[y].ls, cl, mid); tree[x].rs = merge(tree[x].rs, tree[y].rs, mid + 1, cr); push_up(x); return x; }
/// ----------------树+LCA---------------- std::vector<int> E[N], ksons[N]; int Log2[N], fa[22][N], dep[N]; int c[N];
voidinitlca(int u, int fau = 0){ dep[u] = dep[fau] + 1; fa[0][u] = fau; for (int i = 1; i <= Log2[dep[u]]; ++i) fa[i][u] = fa[i - 1][fa[i - 1][u]]; for (auto& v : E[u]) if (v != fau) initlca(v, u); }
// v的k级祖先 intgetast(int v, int k){ k = dep[v] - k; if (k <= 0) return-1; while (dep[v] > k) v = fa[Log2[dep[v] - k]][v]; return v; }
/// ----------------线段树合并---------------- int ans[N]; voiddfs(int u, int fau = 0){ for (auto& v : E[u]) { if (v == fau) continue; dfs(v, u); merge(u, v); }
update(c[u], 1, u); // 添加u的颜色 for (auto& c : ksons[u]) { update(c, -1, u); // 删除u的k+1层儿子的颜色 }
ans[u] = tree[u].val; }
/// ----------------主函数---------------- intmain(){ n = read(); int k = read(); for (int i = 1; i <= n; ++i) { c[i] = read(); }
// 存边和初始化 for (int i = 2; i < N; ++i) Log2[i] = Log2[i / 2] + 1; for (int i = 1; i < n; ++i) { int u = read(), v = read(); E[u].push_back(v); E[v].push_back(u); } initlca(1);
// 统计每个节点的k+1层儿子 for (int i = 1; i <= n; ++i) { int x = getast(i, k + 1); if (x != -1) ksons[x].push_back(c[i]); }
pcnt = n; dfs(1);
for (int q = read(); q--; ) { int x = read(); printf("%d\n", ans[x]); } return0; }
for (int i = 1; i <= n; ++i) { a[i] = read(); insert(i); // 初始化 得到一个满序列 便于删除 } for (int i = n; i >= 1; --i) { int t = kthnum(a[i]); // 当前剩余序列的第 a_i 位是 b 的第 t 位 b[t] = i; // b 的第 t 位是 i remove(t); }
现在得到了排列b,用动态规划求每一时刻的最长上升子序列长度。
首先回顾求最长上升子序列的板子:
1 2 3 4 5 6 7 8
// dp初始化为最大 int mx = 0; for (int i = 1; i <= n; ++i) { int t = std::lower_bound(dp + 1, dp + n + 1, b[i]) - dp; dp[t] = b[i]; mx = std::max(mx, t); } printf("%d\n", mx);
发现上面的 t 就是每时刻的最长上升子序列长度,又由于插入过程是有时间先后的,所以需要将每一时刻的 t 按 b[] 的大小排序,再取最大值输出。
1 2 3 4 5 6 7 8 9
int mx = 0; for (int i = 1; i <= n; ++i) { int t = std::lower_bound(dp + 1, dp + n + 1, b[i]) - dp; dp[t] = b[i]; ans[b[i]] = t; } for (int i = 1; i <= n; ++i) { printf("%d\n", mx = std::max(mx, ans[i])); }
#include<cstdio> #include<algorithm> using ll = longlong; constint N = 3e6 + 8;
inline ll read(){ ll Y = getchar(), S = 0, C = 1; while (Y > 57 || Y < 48) { if (Y == 45) C = -1; Y = getchar(); } while (Y <= 57 && Y >= 48) { S = (S << 3) + (S << 1) + Y - 48; Y = getchar(); } return S * C; }
/// ----------------线段树---------------- structSEGTREE { int val; } tree[N]; int n, pcnt; #define ls p<<1 #define rs p<<1|1 #define mid (cl+cr>>1)
voidupdate(int x, int d, int p = 1, int cl = 1, int cr = n){ if (cl == cr) return tree[p].val += d, void(); if (x <= mid) update(x, d, ls, cl, mid); if (x > mid) update(x, d, rs, mid + 1, cr); tree[p].val = tree[ls].val + tree[rs].val; }
intquery(int l, int r, int p = 1, int cl = 1, int cr = n){ if (cl >= l && cr <= r) return tree[p].val; if (mid >= r) returnquery(l, r, ls, cl, mid); if (mid < l) returnquery(l, r, rs, mid + 1, cr); returnquery(l, r, rs, mid + 1, cr) + query(l, r, ls, cl, mid); }
// 权值线段树 inlinevoidinsert(int x){ update(x, 1); } // 插入 inlinevoidremove(int x){ update(x, -1); } // 删除 intkthnum(int x, int p = 1, int cl = 1, int cr = n){ // 指定排名的数 if (cl == cr) return cl; if (tree[ls].val >= x) returnkthnum(x, ls, cl, mid); // 往左搜 elsereturnkthnum(x - tree[ls].val, rs, mid + 1, cr); // 往右搜 }
/// ----------------主函数---------------- int a[N], b[N], dp[N], ans[N]; intmain(){ n = read(); pcnt = 0; for (int i = 1; i <= n; ++i) { a[i] = read() + 1; insert(i); // 初始化 得到一个满序列 便于删除 dp[i] = 0x3f3f3f3f; // 初始化 dp } for (int i = n; i >= 1; --i) { int t = kthnum(a[i]); // 当前剩余序列的第 a_i 位是 b 的第 t 位 b[t] = i; // b 的第 t 位是 i remove(t); } int mx = 0; for (int i = 1; i <= n; ++i) { int t = std::lower_bound(dp + 1, dp + n + 1, b[i]) - dp; dp[t] = b[i]; ans[b[i]] = t; } for (int i = 1; i <= n; ++i) { printf("%d\n", mx = std::max(mx, ans[i])); } }
#include<cstdio> #include<algorithm> #include<vector> #include<stack> #include<queue> using ll = longlong; constint N = 2e6 + 8; constint MOD = 1e9 + 7;
inline ll read(){ ll Y = getchar(), S = 0, C = 1; while (Y > 57 || Y < 48) { if (Y == 45) C = -1; Y = getchar(); } while (Y <= 57 && Y >= 48) { S = (S << 3) + (S << 1) + Y - 48; Y = getchar(); } return S * C; }
/// ----------------结构体---------------- structnode { int l = 1e9, r; // 吞噬范围 node& operator+=(const node& other) { l = std::min(l, other.l); r = std::max(r, other.r); return *this; } } w[N]; int n, rt[N];
voideachTarjan(int u){ dfsn[u] = low[u] = ++unix; // 记录DFS序 S.push(u); // DP更新low[u] for (auto v : E[u]) { if (!dfsn[v]) eachTarjan(v), low[u] = std::min(low[u], low[v]); elseif (!fa[v]) low[u] = std::min(low[u], dfsn[v]); } // u是SCC的根 if (low[u] == dfsn[u]) { int top; do { top = S.top(); S.pop(); // 出栈 fa[top] = u; // 记录u的子节点的根为u } while (top != u); // 不断弹出直到根u弹出为止 } }
inlinevoidtarjan(int n){ // Tarjan for (int u = 1; u <= n; ++u) { if (!dfsn[u]) eachTarjan(u); } // 重构图 将子树的性质嫁接到SCC的根上 for (int u = 1; u <= n; ++u) { for (auto v : E[u]) { if (fa[u] != fa[v]) Etmp[fa[u]].push_back(fa[v]); } // 存储新的边 wtmp[fa[u]] += w[u]; // 存储新的边权 } for (int u = 1; u <= n; ++u) { w[u] = wtmp[u]; E[u] = Etmp[u]; std::sort(E[u].begin(), E[u].end()); E[u].erase(std::unique(E[u].begin(), E[u].end()), E[u].end()); } }
int deg[N]; inlinevoidtopo(int n){ for (int u = 1; u <= n; ++u) { for (auto v : E[u]) deg[v] += 1; } std::queue<int> Q; for (int u = 1; u <= n; ++u) { if (!deg[u] && u == fa[u]) Q.push(u); } while (!Q.empty()) { int u = Q.front(); Q.pop(); for (auto to : E[u]) { w[to] += w[u]; deg[to] -= 1; if (!deg[to]) { Q.push(to); } } } }
for (int i = 1; i <= n; ++i) { xi[i] = read(), ri[i] = read(); }
build(); for (int i = 1; i <= n; ++i) { int l = std::lower_bound(xi + 1, xi + n + 1, xi[i] - ri[i]) - xi, // 二分找第一个大于等于 xi[i]-ri[i] 的 l 即是下界 r = std::upper_bound(xi + 1, xi + n + 1, xi[i] + ri[i]) - xi - 1; // 二分找第一个大于 xi[i]+ri[i] 的 r 即是上界上面的那一个 再 -1 即是上界 addedge(l, r, rt[i]); // Slime i will eat [l,r] // rt[] 数组表示 i 在新图上点的编号是 rt[i] }
tarjan(n << 2), topo(n << 2); // 缩个点再跑一圈
ll ans = 0; for (int i = 1; i <= n; ++i) { ans += 1ll * i * (w[fa[rt[i]]].r - w[fa[rt[i]]].l + 1); ans %= MOD; } printf("%lld", ans);
#include<cstdio> #include<vector> using ll = longlong; constint N = 1e6 + 8, M = 2.5e7 + 8;
inline ll read(){ ll Y = getchar(), S = 0, C = 1; while (Y > 57 || Y < 48) { if (Y == 45) C = -1; Y = getchar(); } while (Y <= 57 && Y >= 48) { S = (S << 3) + (S << 1) + Y - 48; Y = getchar(); } return S * C; }
/// ----------------线段树---------------- structSEGTREE { int val; // 区间最大值 int id; // 区间最大值编号 int ls, rs; } tree[M]; int n, pcnt; // 递归起点 节点时间戳 #define mid (cl+cr>>1)
// 在这两个函数中写需要维护的东西 inlinevoidpush_up(SEGTREE& p, SEGTREE l, SEGTREE r){ if (l.val >= r.val) { p.val = l.val; p.id = l.id; } else { p.val = r.val; p.id = r.id; } } inlinevoidpush_down(int& p, int val, int id){ if (!p) p = ++pcnt; tree[p].val += val; tree[p].id = id; }