voiddfs1(int x){ for (int y : g[x]) { dfs1(y); if (len[y] > len[son[x]]) son[x] = y; } len[x] = len[son[x]] + 1; } voiddfs2(int x, int l = 0){ if (!son[x]) lp.push_back(l); else { dfs2(son[x], l + 1); for (int y : g[x]) if (y != son[x]) dfs2(y, 1); } }
intmain(){ ios::sync_with_stdio(false); cin.tie(nullptr); int T; cin >> T; while (T--) { cin >> n;
lp.clear(); for (int x = 1; x <= n; ++x) son[x] = len[x] = 0, g[x].clear();
for (int i = 2, x; i <= n; ++i) cin >> x, g[x].push_back(i); dfs1(1); dfs2(1, 1); sort(lp.begin(), lp.end(), greater<int>()); int p = lp.size(), ans = p; for (int i = 0; i < p; ++i) ans = min(ans, lp[i] + i); cout << ans << '\n'; } return0; }
#define fec(i, x, y) (int i = head[x], y = g[i].to; i; i = g[i].ne, y = g[i].to) #define dbg(...) fprintf(stderr, __VA_ARGS__) #define fi first #define se second
using ll = longlong; using ull = unsignedlonglong; using pii = pair<int, int>;
template <typename A, typename B> boolsmax(A &a, const B &b){ return a < b ? a = b, 1 : 0; } template <typename A, typename B> boolsmin(A &a, const B &b){ return b < a ? a = b, 1 : 0; }
int n, m, S, T, nod; char s[N][N]; int sc[N], cl[N];
structEdge {int to, ne, f;} g[M << 1]; int head[N], tot = 1; voidaddedge(int x, int y, int z){ g[++tot].to = y; g[tot].f = z; g[tot].ne = head[x]; head[x] = tot; } voidadde(int x, int y, int z){ addedge(x, y, z); addedge(y, x, 0); }
int dis[N], gap[N], cur[N], q[N]; voidbfs(){ int hd = 0, tl = 0; q[++tl] = T, ++gap[dis[T] = 1]; while (hd < tl) { int x = q[++hd]; forfec(i, x, y)if(!dis[y] && g[i^1].f) dis[y] = dis[x] + 1, ++gap[dis[y]], q[++tl] = y; } } intdfs(int x, int a){ if (x == T || !a) return a; int flow = 0, f; for (int &i = cur[x]; i; i = g[i].ne) if (dis[x] == dis[g[i].to] + 1 && (f = dfs(g[i].to, std::min(a, g[i].f)))) { g[i].f -= f, g[i ^ 1].f += f; a -= f, flow += f; if (!a) return flow; } --gap[dis[x]]; if (!gap[dis[x]]) dis[S] = nod + 1; ++gap[++dis[x]]; return flow; } intISAP(){ staticint ans = 0; bfs(); while (dis[S] <= nod) memcpy(cur + 1, head + 1, sizeof(*head) * nod), ans += dfs(S, INF); return ans; }
intmain(){ ios::sync_with_stdio(false); cin.tie(nullptr), cout.tie(nullptr); int c, d, mxk = 0, sum = 0; cin >> n >> m >> c >> d; for (int i = 1; i <= n; ++i) { cin >> (s[i] + 1); for (int j = 1; j <= m; ++j) s[i][j] = s[i][j] == '.', sc[i] += s[i][j], cl[j] += s[i][j], smax(mxk, cl[j]); smax(mxk, sc[i]), sum += sc[i]; }
S = n + m + 1, T = nod = S + 1; for (int i = 1; i <= n; ++i) adde(S, i, 0); for (int i = 1; i <= m; ++i) adde(i + n, T, 0); for (int i = 1; i <= n; ++i) for (int j = 1; j <= m; ++j) if (s[i][j]) adde(i, j + n, 1);
ll ans = LLONG_MAX; for (int k = 0; k <= mxk; ++k) { if (k) { memset(gap + 1, 0, sizeof(*gap) * nod); memset(dis + 1, 0, sizeof(*dis) * nod); for (int i = 1; i <= n; ++i) ++g[i * 2].f; for (int i = 1; i <= m; ++i) ++g[(i + n) * 2].f; } smin(ans, (ll)c * k + (ll)d * (sum - ISAP())); } cout << ans << '\n'; return0; }
在一条链上,如果没有建立过桥的点,那么它们只能继续走下去,没有其余选择。所以我们可以把每个点合并到右边的第一个有桥的点上,即,假设链 a 上有桥的点分别为 pa,1,pa,2,⋯,pa,k(令 pa,k+1=m+1),那么我们可以把 pa,i+1 到 pa,i+1(0≤i≤k) 中的点都是用一个点 va,i 来代表。我们对于每条链使用一个 set 维护现在压缩后的点,可以 log 查询一个点被压缩到哪个点了。
然后我们来看操作 1。对于需要新建的桥 (a,b)↔(a′,b),如果 (a,b),(a′,b) 不在各自被压缩后的点集中,那么就加进压缩后的点集,同时需要在 LCT 上维护变化。具体的,如果 (a,b) 这个点原先被压缩进了 v 点,其代表区域为 [l,r],那么我们需要新建一个点 u 来代表 [b+1,r],那么这时我们需要修改 v 点的代表范围为 [l,b]。这里我们仍然用 v 来代表 (a,b) ,因为如果修改 (a,b) 所属的压缩点的编号,就需要修改原先 v 的孩子的父亲,这个过程难以维护。然后,设原来的 v 的父节点是 fa,我们需要将 v 和 fa 之间的边断开,然后连接 v→u,u→fa。
所以我们令 f[i][j][k] 表示跳跃了从任意一个排列上的 i 出发,最多2k 步,位于第 j 个排列上的最靠前的位置。
首先是预处理,当 k=0 时,也就是从 i 点出发,跳跃一步,能到达第 j 个排列上的最靠前的位置。
我们枚举我们出发时 i 所在的排列,那么从 i 排列上点 x 出发,走一步能到排列 j 的方案就应该是 i 排列上位于 x 右边的所有点 y 在 j 排列上的位置的最小值。
而 f[i][j][k] 就是从每一个排列上的 i 出发的答案的最小值。
接下来的转移非常简单,只需要枚举一个中转排列 l,那么 f[i][j][k] 就是 f[i][j][k−1],和从在 l 上 f[i][l][k−1] 位置的点出发走 2k−1 步到 j 链的最靠前位置的最小值。形式化一下,设 ul 就是 f[i][l][k−1] 在 l 链上对应的点,那么 f[i][j][k]=minl{f[i][j][k−1],f[ul][j][k−1]}。
最后考虑询问怎么处理。
对于询问 x,y,求出首先判断 x 能否直接接上 y,如果可以答案就是 1。然后开始倍增,维护 pi(1≤i≤m) 表示从 x 开始,在尚不能到达 y 的情况下,在第 i 个排列上能到达的最靠前的位置,记录 ans 表示此时走的总步数。然后从大到小枚举 k,用类似前面倍增转移的方法去构造 gi 为在 p 的基础上再走最多 2k 步,在 i 排列上的最靠前的位置。判断构造出来的 g 能否直接到达 y,如果可以就不更新 p,否则用 g 替换 p 并将 ans 加上 2k。最后的答案应该是 ans+2。(因为 ans 中计算的是跳跃数不是经过的点数,同时 ans 求出的是最大的不能跳跃到 y 前面的步数)
#define fec(i, x, y) (int i = head[x], y = g[i].to; i; i = g[i].ne, y = g[i].to) #define dbg(...) fprintf(stderr, __VA_ARGS__) #define fi first #define se second
using ll = longlong; using ull = unsignedlonglong; using pii = pair<int, int>;
template <typename A, typename B> boolsmax(A &a, const B &b){ return a < b ? a = b, 1 : 0; } template <typename A, typename B> boolsmin(A &a, const B &b){ return b < a ? a = b, 1 : 0; }
constexprint N = 1e5 + 7; constexprint M = 7; constexprint LG = 17;
int n, m; int a[M][N], r[M][N], f[N][M][LG];
intmain(){ ios::sync_with_stdio(false); cin.tie(nullptr), cout.tie(nullptr); cin >> n >> m; for (int i = 0; i < m; ++i) for (int j = 1; j <= n; ++j) cin >> a[i][j], r[i][a[i][j]] = j;
for (int i = 1; i <= n; ++i) for (int j = 0; j < m; ++j) f[i][j][0] = n + 1; for (int i = 0; i < m; ++i) for (int j = 0; j < m; ++j) { int p = n + 1; for (int k = n; k; --k) smin(p, r[j][a[i][k]]), smin(f[a[i][k]][j][0], p); } for (int k = 1; k < LG; ++k) for (int i = 1; i <= n; ++i) for (int j = 0; j < m; ++j) { f[i][j][k] = f[i][j][k - 1]; for (int l = 0; l < m; ++l) smin(f[i][j][k], f[a[l][f[i][l][k - 1]]][j][k - 1]); }
int q; cin >> q; while (q--) { int x, y; cin >> x >> y; int p[M], ans = 0, has_ans = 0; for (int j = 0; j < m; ++j) { p[j] = r[j][x]; if (r[j][x] <= r[j][y]) { ans = -1, has_ans = 1; goto print; } } for (int i = LG - 1; ~i; --i) { int g[M], flag = 0; for (int j = 0; j < m; ++j) { g[j] = p[j]; for (int k = 0; k < m; ++k) smin(g[j], f[a[k][p[k]]][j][i]); if (g[j] <= r[j][y]) { flag = has_ans = 1; break; } } if (!flag) ans += 1 << i, memcpy(p, g, sizeof(p)); } print: if (has_ans) cout << ans + 2 << '\n'; else cout << "-1\n"; }
#define fec(i, x, y) (int i = head[x], y = g[i].to; i; i = g[i].ne, y = g[i].to) #define dbg(...) fprintf(stderr, __VA_ARGS__) #define fi first #define se second
using ll = longlong; using ull = unsignedlonglong; using pii = pair<int, int>;
template <typename A, typename B> boolsmax(A &a, const B &b){ return a < b ? a = b, 1 : 0; } template <typename A, typename B> boolsmin(A &a, const B &b){ return b < a ? a = b, 1 : 0; }
intgetnum(vector<int> &c){ int cnt = 0; for (int x : c) if (x) ++cnt; return cnt; }
intmain(){ ios::sync_with_stdio(false); cin.tie(nullptr), cout.tie(nullptr); int T; cin >> T; while (T--) { int n, x, y, z; cin >> n >> x >> y >> z; vector<int> a(n), c(n); for (int i = 0; i < n; ++i) cin >> a[i], ++c[a[i]]; string ans(n, '0'); string anso; vector<int> ansn; if (x) { stack<int> num; stack<char> op; int ce = 0, c2 = 0, mnp = n; for (int i = 0; i < n; ++i) ce += c[i] > 0, c2 += c[i] > 1, c[i] && smin(mnp, i); if (ce >= y + z) { for (int i = n - 1; ~i; --i) if (c[i]) { if (y) --y, op.push('|'); elseif (z) --z, op.push('^'); elsebreak; --c[i], num.push(i), ans[i] = '1'; } } else { assert(c2); if (y) { int p = -1; for (int i = 0; i < n; ++i) if (c[i] > 1) p = i; --c[p], num.push(p), ans[p] = '1', --y, op.push('|'); for (int i = n - 1; ~i; --i) if (c[i] && i != p) { if (y) --y, op.push('|'); elseif (z) --z, op.push('^'); elseassert(0); --c[i], num.push(i), ans[i] = '1'; } } else { for (int i = n - 1; ~i; --i) if (c[i]) { if (y) --y, op.push('|'); elseif (z) --z, op.push('^'); elseassert(0); --c[i], num.push(i), ans[i] = '1'; } if (x > 1 && c2 > 1) { int p1 = find_if(c.begin(), c.end(), [](constint &x) { return x > 0; }) - c.begin(); int p2 = find_if(c.begin() + p1 + 1, c.end(), [](constint &x) { return x > 0; }) - c.begin(); --c[p1], num.push(p1), --x, op.push('&'); --c[p2], num.push(p2), --x, op.push('&'); } elseif (c2 == 1) { int p = -1; for (int i = 0; i < n; ++i) if (c[i] > 0) p = i; if ((c[p] - x) & 1) { assert(num.top() == mnp); num.pop(), op.pop(); ++z, ++c[mnp], ans[mnp] = '0'; --c[mnp], num.push(mnp), op.push('&'), --x; } } elseif (x == 1) { int p = find_if(c.begin(), c.end(), [](constint &x) { return x & 1; }) - c.begin(); if (p < n) --c[p], num.push(p), --x, op.push('&'); else { assert(num.top() == mnp); num.pop(), op.pop(); ++z, ++c[mnp], ans[mnp] = '0'; --c[mnp], num.push(mnp), op.push('&'), --x; } } } }
for (int i = n - 1; ~i; --i) while (c[i]) { if (x) --x, op.push('&'); elseif (y) --y, op.push('|'); elseif (z) --z, op.push('^'); elseassert(0); --c[i]; num.push(i); } while (!op.empty()) anso.push_back(op.top()), op.pop(); while (!num.empty()) ansn.push_back(num.top()), num.pop(); } else { bool odd_enough = 0; for (int i = n - 1; ~i; --i) if (c[i] & 1) while (c[i]) { if (z) --z, anso.push_back('^'); elsegoto odd_skip; ans[i] = '1', ansn.push_back(i); --c[i]; } odd_enough = 1; odd_skip: if (!odd_enough) { for (int i = n - 1; ~i; --i) while (c[i]) { if (y) --y, anso.push_back('|'); elseassert(0); ans[i] = '1', ansn.push_back(i); --c[i]; } } else { stack<int> num; stack<char> op; for (int i = n - 1; ~i; --i) if (c[i]) { if (y) --y, op.push('|'); elsebreak; --c[i], num.push(i), ans[i] = '1'; } for (int i = n - 1; ~i; --i) while (c[i]) { if (y) --y, op.push('|'); elseif (z) --z, op.push('^'); --c[i], num.push(i); } while (!op.empty()) anso.push_back(op.top()), op.pop(); while (!num.empty()) ansn.push_back(num.top()), num.pop(); } } end: reverse(ans.begin(), ans.end()); cout << ans << '\n'; for (int i = 0; i < n; ++i) cout << anso[i]; cout << "\n"; for (int i = 0; i < n; ++i) cout << ansn[i] << " \n"[i == n - 1]; } return0; }
首先我们需要最大化的答案是 il,因此如果我们倒序枚举 i,那么 l 只有变大,才有更新答案的可能。因为我们知道 l 对于花费的限制是有单调性的,如果 l 不满足限制,那么 l 右边的所有点都不可能满足限制。
因此我们倒序枚举 i 的过程中,维护 p 表示我们本来应该在二分中求出的横坐标 l。每次 i 变化时,如果我们发现现在的 p 不满足限制,我们就跳过 i,因为我们将 p 变小不可能对答案产生更新。如果发现现在的 p 满足限制了,我们可以逐渐增大 p,直到不满足限制位置,在 p 变化的期间,记录好对答案可能产生的更新。这样,我们就可以避免一次二分了。
随后我们可以发现,如果我们按照 l 从右向左倒序求出凸包,在求出凸包的过程中,对于新加入的 l,我们会在单调栈中弹出若干点,然后压入一个点,此时的凸包就是横坐标在 l 右边的点构成的凸包。如果我们记录下来的被我们弹出的点,那么这个过程是可以恢复的。在求完凸包后,再从右向左扫过去,只需要把单调栈的栈顶弹出,把这一个 l 出弹出的点再压入,就能还原出在 l 右边的点构成的凸包,而且总计的操作次数和建凸包是相通的。
把这个方法和上面的 p 逐渐增加的思路结合,我们就不必使用线段树来维护凸包了。随着 p 的增大,我们也只需要把 p 所在的位置的凸包还原出来。
#define fec(i, x, y) (int i = head[x], y = g[i].to; i; i = g[i].ne, y = g[i].to) #define dbg(...) fprintf(stderr, __VA_ARGS__) #define fi first #define se second
using ll = longlong; using ull = unsignedlonglong; using pii = pair<int, int>;
template <typename A, typename B> boolsmax(A &a, const B &b){ return a < b ? a = b, 1 : 0; } template <typename A, typename B> boolsmin(A &a, const B &b){ return b < a ? a = b, 1 : 0; }
constexprint N = 5000 + 7; constexprint M = 1e5 + 7; constexprint INF = INT_MAX;
int n, m, T, na, nb, tp; pii a[M], b[M], h[M]; vector<pii> del[M];
voidinit(int n, pii *a, int &na){ staticint x[N], v[N], c[M]; int mxx = 0; for (int i = 1; i <= n; ++i) cin >> x[i], smax(mxx, x[i]); for (int i = 1; i <= n; ++i) cin >> v[i]; for (int i = 0; i <= mxx; ++i) c[i] = INF; for (int i = 1; i <= n; ++i) for (int j = i; j <= n; ++j) smin(c[x[j] - x[i]], v[i] + v[j]); for (int i = 0; i <= mxx; ++i) if (c[i] < INF) a[++na] = pii(i, c[i]);//, cerr << i << ' ' << c[i] << endl; }
pii operator - (pii a, pii b) { returnpii(a.fi - b.fi, a.se - b.se); } ll cross(pii a, pii b){ return (ll)a.fi * b.se - (ll)a.se * b.fi; } voidgetHell(){ for (int i = nb; i; --i) { while (tp > 1 && cross(b[i] - h[tp], h[tp] - h[tp - 1]) <= 0) del[i].push_back(h[tp--]); h[++tp] = b[i]; } }
ll calc(pii a, pii b){ return (ll)a.fi * b.se + (ll)a.se * b.fi; } boolcheck(vector<pii> &h, pii cur, ll c){ int l = 1, r = h.size() - 1; while (l < r) { int mid = (l + r) / 2; if (calc(cur, h[mid]) <= calc(cur, h[mid + 1])) r = mid; else l = mid + 1; } returncalc(cur, h[l]) <= c; }
intmain(){ ios::sync_with_stdio(false); cin.tie(nullptr), cout.tie(nullptr); cin >> n >> m >> T; init(n, a, na), init(m, b, nb); getHell();
while (T--) { ll ans = 0, c; cin >> c; vector<pii> h2(h, h + tp + 1); for (int i = na, p = 1; i; --i) { while (p <= nb && check(h2, a[i], c)) { smax(ans, (ll)a[i].fi * b[p].fi); assert(h2.back() == b[p]); h2.pop_back(); copy(del[p].rbegin(), del[p].rend(), back_inserter(h2)); ++p; } } cout << ans << '\n'; } return0; }