T1
缆车
有向图中从 \(1\) 可达所有点等价于除 \(1\) 以外所有点都有入度。离散化之后容易求出每个左端点对应的最大合法右端点,求出这些区间之后对于一个询问,要么两边都扩展,要么只扩展一边。几种情况分别线段树维护即可。
代码
#include <iostream>
#include <algorithm>
#include <string.h>
#include <time.h>
#include <set>
#define lowbit(x) ((x) & (-(x)))
#define int long long
using namespace std;
#define getchar() p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;
char buf[1<<21],*p1,*p2,ch;
long long read(){long long ret=0;char c=getchar();while(c<'0'||c>'9')c=getchar();while(c>='0'&&c<='9')ret=ret*10+c-'0',c=getchar();return ret;
}
const int inf = 0x3f3f3f3f3f3f3f3f;
inline void Cmin(int &x, int y) { x = min(x, y); }
int n, m, q, P;
multiset<int> st;
struct Edge {int u, v, w;
} e[300005];
int cur[300005];
int X[400005];
int d[1200005], dcnt;
inline int mp(int x) { return lower_bound(d + 1, d + dcnt + 1, x) - d; }
int R[1200005];
struct qquery {int l, r, id;
} qs[1200005];
int qcnt;
struct PBIT {int bit[1200005];void add(int x, int y) { for (; x <= 1200000; x += lowbit(x)) Cmin(bit[x], y); }int query(int x) {int ret = inf;for (; x; x -= lowbit(x)) Cmin(ret, bit[x]);return ret;}void clear() { memset(bit, 63, sizeof bit); }
} bit2, bit3;
struct SBIT {int bit[1200005];void add(int x, int y) { for (; x; x -= lowbit(x)) Cmin(bit[x], y); }int query(int x) {int ret = inf;for (; x <= 1200000; x += lowbit(x)) Cmin(ret, bit[x]);return ret;}void clear() { memset(bit, 63, sizeof bit); }
} bit0, bit1;
int ans[400005];
int ql[400005], qr[400005];
signed main() {int ttt = clock();freopen("car.in", "r", stdin);freopen("car.out", "w", stdout);bit0.clear(), bit1.clear();bit2.clear(), bit3.clear();n = read(), m = read(), P = read();for (int i = 1; i <= m; i++) e[i].u = read(), e[i].v = read(), e[i].w = d[++dcnt] = read();q = read();for (int i = 1; i <= q; i++) {int l = read(), r = read(); X[i] = read();d[++dcnt] = l, d[++dcnt] = r;ql[i] = l, qr[i] = r;}d[++dcnt] = inf;sort(d + 1, d + dcnt + 1);dcnt = unique(d + 1, d + dcnt + 1) - d - 1;for (int i = 2; i <= n; i++) st.insert(cur[i] = inf);sort(e + 1, e + m + 1, [](Edge a, Edge b) { return a.w < b.w; });for (int i = m; i; i--) {int x = e[i].v;st.erase(st.find(cur[x]));cur[x] = e[i].w;st.insert(cur[x]);R[mp(e[i].w)] = mp(*st.rbegin());}for (int i = 1; i <= m; i++) qs[++qcnt] = (qquery) { mp(e[i].w), R[mp(e[i].w)], -1 };for (int i = 1; i <= q; i++) qs[++qcnt] = (qquery) { mp(ql[i]), mp(qr[i]), i };sort(qs + 1, qs + qcnt + 1, [](qquery a, qquery b) { return a.l == b.l ? (a.id < b.id) : (a.l < b.l); });memset(ans, 63, sizeof ans);for (int i = 1; i <= qcnt; i++) {if (qs[i].id > 0) {Cmin(ans[qs[i].id], bit1.query(qs[i].r) + d[qs[i].l] - d[qs[i].r]);Cmin(ans[qs[i].id], bit2.query(qs[i].r) + d[qs[i].l]);} else {bit1.add(qs[i].r, d[qs[i].r] - d[qs[i].l]);bit2.add(qs[i].r, -d[qs[i].l]);}}sort(qs + 1, qs + qcnt + 1, [](qquery a, qquery b) { return a.l == b.l ? (a.id < b.id) : (a.l > b.l); });for (int i = 1; i <= qcnt; i++) {if (qs[i].id > 0) {Cmin(ans[qs[i].id], bit0.query(qs[i].r) - d[qs[i].r]);Cmin(ans[qs[i].id], bit3.query(qs[i].r));} else {bit0.add(qs[i].r, d[qs[i].r]);bit3.add(qs[i].r, 0);}}for (int i = 1; i <= q; i++) cout << (ans[i] <= X[i] ? "Yes\n" : "No\n");cerr << (1.0 * clock() - ttt) / CLOCKS_PER_SEC << "\n";return 0;
}
T2
大
容易发现当序列足够长时可以使用不超过 \(5\) 次操作将一个数移到它该在的位置上。具体地,如果这个数原来的位置不能走到它该在的位置,就先调整 \(x\) 的位置再用 \(x\) 跟它交换一次。然后调整 \(x\) 的位置,再把 \(x\) 换到这个数该在的位置,然后交换 \(x\) 和这个数。会发现只要剩余序列长度 \(\ge 4\) 且未排好的序列是连续的,则这个操作就可以做下去。因此每次选择未被排好序的最大值或最小值把它放好即可。最后剩余长度为 \(3\),先把 \(x\) 放到它该在的位置,然后剩下两个若是逆序则不合法,否则合法。因为操作不会改变逆序对数与 \(x\) 之和的奇偶性,所以做到最后如果剩一个逆序则一定不合法。
代码
#include <iostream>
#include <string.h>
#include <cassert>
#include <vector>
#include <array>
#define lowbit(x) ((x) & (-(x)))
#define int long long
using namespace std;
int n, X;
int p[300005], _p[300005];
vector<array<int, 2> > vec;
void op(int x, int y) {if (x != y) {swap(p[x], p[y]);swap(_p[p[x]], _p[p[y]]);vec.emplace_back((array<int, 2>) { x, y });}
}
struct BIT {int bit[300005];void add(int x, int y) { for (; x; x -= lowbit(x)) bit[x] += y; }int query(int x) {int ret = 0;for (; x <= 300000; x += lowbit(x)) ret += bit[x];return ret;}void clear() { memset(bit, 0, sizeof bit); }
} bit;
signed main() {freopen("big.in", "r", stdin);freopen("big.out", "w", stdout);int tc;cin >> tc;while (tc--) {bit.clear();vec.clear();cin >> n >> X;int cnt = 0;for (int i = 1; i <= n; i++) cin >> p[i], _p[p[i]] = i, cnt += bit.query(p[i]), bit.add(p[i], 1);int c = _p[X]; cnt += c;if ((cnt & 1) != (X & 1)) {cout << "NO\n";continue;}for (int i = 1, l = 1, r = n; i <= n - 3; i++) {int x, s;if (l != X) s = l, x = _p[l], ++l;else s = r, x = _p[r], --r;if (!((x ^ s) & 1)) {if ((c ^ x) & 1) op(c, x), swap(c, x);else {int t = 0;for (int k = r; k >= l; k--) {if (((k ^ c) & 1) && k != x && k != c) {t = k;break;}}op(c, t), c = t;op(c, x), swap(c, x);}}if (!((c ^ s) & 1)) {int t = 0;for (int k = r; k >= l; k--) {if (((k ^ c) & 1) && k != x && k != c) {t = k;break;}}op(c, t), c = t;}op(c, s), c = s;op(c, x), swap(c, x);}if (!((c ^ X) & 1)) {int t = 0;for (int k = n; k; k--) {if (((k ^ c) & 1) && k != c && p[k] != k) {t = k;break;}}op(c, t), c = t;}op(c, X);cout << "YES\n";cout << vec.size() << "\n";for (auto v : vec) cout << v[0] << " " << v[1] << "\n";}return 0;
}
T3
邮局
朴素的贪心是如果一个点有多个包裹,先送走目的地更远的。但是这没法维护,我们考虑对每条边算贡献。我们考虑一条边最后被经过的时间,然后把所有边取 \(\max\)。接下来只考虑一条边的贡献。设经过这条边的包裹到这条边起点的距离为 \(d_{1, 2, \cdots k}\)(按离开顺序排列),则每个包裹离开这条边的时间 \(t_i = \max\{t_{i - 1}, d_i\} + 1\),也就是 \(t_k = \max\{ d_i + k - t + 1 \}\)。若要最小化这个式子,显然应该视 \(d\) 为升序,此时每个包裹的贡献即为其距离加上距离 \(\ge\) 它的包裹个数。只需要维护这个即可。在树上可以直接线段树合并,基环树上可以破环为链,此时只需要保证每条边被覆盖的集合正确。把链拼一份到后面,则顺序只需要 \(s \rightarrow t\),逆序拆成就 \(s + n \rightarrow t, s \rightarrow 1\) 即可。
代码
#include <iostream>
#include <vector>
using namespace std;
#define getchar() p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++
char buf[1<<21], *p1, *p2, ch;
long long read() {long long ret = 0, neg = 0; char c = getchar(); neg = (c == '-');while (c < '0' || c > '9') c = getchar(), neg |= (c == '-');while (c >= '0' && c <= '9') ret = ret * 10 + c - '0', c = getchar();return ret * (neg ? -1 : 1);
}
int n, m, ans;
vector<int> G[400005];
int fa[400005];
int dsu[400005], dep[400005], top[400005], dfn[400005], *L = dfn, R[400005];
int op[400005], rt[400005], ncnt;
vector<int> del[400005];
struct node {int mx, s, l, r;
} T[10000005];
struct Segment_Tree {int ncnt;void pushup(int o){T[o].s = T[T[o].l].s + T[T[o].r].s;T[o].mx = max(T[T[o].r].mx, T[T[o].l].mx + T[T[o].r].s);}void Add(int &o, int l, int r, int x, int y){if (!o) o = ++ncnt;if (l == r) return T[o].s += y, T[o].mx = (T[o].s ? l + T[o].s : 0), void();int mid = (l + r) >> 1;if (x <= mid) Add(T[o].l, l, mid, x, y);else Add(T[o].r, mid + 1, r, x, y);pushup(o);}int Merge(int p,int q,int l,int r){if (!p || !q) return p | q;if (l == r){T[p].s += T[q].s, T[p].mx = (T[p].s ? l + T[p].s : 0);return p;}int mid = (l + r) >> 1;T[p].l = Merge(T[p].l, T[q].l, l, mid);T[p].r = Merge(T[p].r, T[q].r, mid + 1, r);pushup(p);return p;}
}seg;
int getf(int x) { return (dsu[x] == x ? x : (dsu[x] = getf(dsu[x]))); }
void dfs(int x){L[x] = ++ncnt;for (int v : G[x]) {dep[v] = dep[x] + 1;top[v] = (x ? top[x] : v);dfs(v);}R[x] = ncnt;
}
inline void add(int x, int y){del[x].emplace_back(y),++op[y];}
void dfs1(int x){for (int v : G[x]) dfs1(v), rt[x] = seg.Merge(rt[x], rt[v], 0, n << 1);seg.Add(rt[x], 0, n<<1, dep[x], op[x]);for (int v : del[x]) seg.Add(rt[x], 0, n << 1, dep[v], -1);ans = max(ans, T[rt[x]].mx - dep[x]);
}
int main() {freopen("postoffice.in", "r", stdin);freopen("postoffice.out", "w", stdout);n = read();for (int i = 1; i <= n; i++) dsu[i] = i;for (int i = 1; i <= n; i++) fa[i + n] = (fa[i] = read()) + n, dsu[getf(i)] = getf(fa[i]);for (int i = 1; i <= n; i++){if (getf(i) == i){int x = i;for(; dsu[x]; x = fa[x]) dsu[x]=0;fa[x + n] = fa[x], fa[x] = 0;}}for (int i = 1; i <= n * 2; i++) G[fa[i]].emplace_back(i);dfs(0);m = read();for(int x, y, i = 1; i <= m; i++){x = read(), y = read();if (L[y] <= dfn[x] && dfn[x] <= R[y]) add(y,x);else if (L[y] <= dfn[x + n] && dfn[x + n] <= R[y]) add(y, x + n), add(top[y], x);else cout << "-1\n", exit(0);}dfs1(0);cout << ans << "\n";return 0;
}