CSP-S 2022题解

闲话#

\(\huge{寄}\)

\(\text{T1}\) 极限脑抽,删掉暴搜打错解,\(70pts \to 0pts\)

\(\text{T2}\) 洛谷 \(100pts\)\(\infin\) \(40pts\), 很慌;

\(\text{T3}\) 差点想到正解(但确实没想到)暴力 \(40pts\)

\(\text{T4}\) 甚至一分都拿不到;

期望 \(110\)

打的模拟赛真的太少了(似乎就打过四五次……),心态真的太容易被影响(又是心态炸裂导致的水平急剧下跌)。

题解#

\(\bold{T1}\)

\(n \le 2500\),可以往 \(O(n^2)\) 考虑。

那么对于这题而言,\(O(n^2)\) 能干什么呢?

  • 对每个点进行一遍 \(\text{BFS}\),预处理出它能到达的点集
  • 枚举 \(4\) 个点中的 \(2\)

突破口也就在第二点中——对于 \(4\) 个点,我们不妨把它们拆成 \(2 + 2\) 个点,预处理出 \(w[i]\)(从 \(1\) 开始走 \(2\) 步到达 \(i\) 点的最大值),然后枚举 \(B, C\) 两点更新答案即可。

\(\large{\text{BUT}}\)

事情真的这么简单吗?

题目:请帮小熊规划一个行程,使得小熊访问的四个不同景点的分数之和最大。

注意所选的四个点是不同的,那么在更新答案时就不能只是简单地取两边的最大值。

那么为什么会出现重复的情况呢?显然是我们没有枚举到 \(A, D\) 导致的。但是现阶段的时间复杂度又不允许我们枚举 \(A, D\),怎么办呢?

我们要使答案最大,显然最大值选不了就会选次大值,次大值选不了就会选次次大值,次次大值选不了就会选……若最坏情况下 \(A\) 点会和其他点发生 \(n\) 次重复,那么就可以通过维护前 \(n + 1\) 大值来解决重复的问题。

由于图中不存在自环,所以 \(B\) 点势必不会和 \(A\) 点重复,但 \(C, D\) 两点都是可以和 \(A\) 重复的。\(D\) 点情况同理,而对于 \(B\) 点,枚举了 \(C\) 点,所以可以保证 \(C \ne B\),能与 \(B\) 点重复的点就只剩下 \(D\) 点一个点。\(C\) 点同理。因此,我们可以维护 \(w[i][0/1/2]\)(从 \(1\) 开始走 \(2\) 步到达 \(i\) 点的最大/次大/次次大值),更新答案时分类讨论一下就好啦~

$Code$
#include <bits/stdc++.h>

#define MAXN 2600
#define MAXM 100100

using namespace std;

typedef long long ll;

int n, m, p, dis[MAXN], mx[MAXN][4];
int tot, head[MAXN];
ll w[MAXN];
bool vis[MAXN], G[MAXN][MAXN];

struct Edge {
	int to, nxt;
} e[MAXM << 1];

template<typename _T>
void read(_T &_x) {
    _x = 0;
    _T _f = 1;
    char _ch = getchar();
    while (_ch < '0' || '9' < _ch) {
        if (_ch == '-') _f = -1;
        _ch = getchar();
    }
    while ('0' <= _ch && _ch <= '9') {
        _x = (_x << 3) + (_x << 1) + (_ch & 15);
        _ch = getchar();
    }
    _x *= _f;
}

template<typename _T>
void write(_T _x) {
    if (_x < 0) {
        putchar('-');
        _x = -_x;
    }
    if (_x > 9) write(_x / 10);
    putchar('0' + _x % 10);
}

void add(int u, int v) {
	e[++tot] = Edge{v, head[u]};
	head[u] = tot;
}

void bfs(int s) {
	queue<int> q;
	memset(vis, 0, sizeof(vis));
	vis[s] = 1, dis[s] = 0;
	q.push(s);
	while (!q.empty()) {
		int u = q.front();
		q.pop();
		if (dis[u] > p) return;
		for (int i = head[u], v; i; i = e[i].nxt) {
			v = e[i].to;
			if (vis[v]) continue;
			vis[v] = 1;
			dis[v] = dis[u] + 1;
			G[s][v] = 1;
			q.push(v);
		}
	}
}

int main() {
	read(n), read(m), read(p);
	for (int i = 2; i <= n; i++) read(w[i]);
	while (m--) {
		int u, v;
		read(u), read(v);
		add(u, v), add(v, u);
	}
	for (int i = 1; i <= n; i++) bfs(i);
	w[0] = -1e18;
	memset(vis, 0, sizeof(vis));
	for (int i = 2; i <= n; i++) {
		if (!G[1][i]) continue;
		for (int j = 2; j <= n; j++) {
			if (!G[i][j]) continue;
			vis[j] = 1;
			if (w[i] > w[mx[j][0]]) {
				mx[j][2] = mx[j][1], mx[j][1] = mx[j][0], mx[j][0] = i;
			} else if (w[i] > w[mx[j][1]]) {
				mx[j][2] = mx[j][1], mx[j][1] = i;
			} else if (w[i] > w[mx[j][2]]) mx[j][2] = i;
		}
	}
	ll ans = 0;
	for (int B = 2; B <= n; B++) {
		if (!vis[B]) continue;
		for (int C = B + 1; C <= n; C++) {
			if (!G[B][C] || !vis[C]) continue;
			ll tmp;
			int i = 0, k = 0;
			if (mx[B][i] == C) i++;
			if (mx[C][k] == B) k++;
			if (mx[B][i] != mx[C][k]) tmp = w[mx[B][i]] + w[mx[C][k]];
			else {
				int j = i + 1, l = k + 1;
				if (mx[B][j] == C) j++;
				if (mx[C][l] == B) l++;
				tmp = max(w[mx[B][i]] + w[mx[C][l]], w[mx[B][j]] + w[mx[C][k]]);
			}
			ans = max(ans, w[B] + w[C] + tmp);
		}
	}
	write(ans);
	return 0;
}

\(\bold{T2}\)

对于特殊条件 1:小 L 一定选最大的,小 Q 一定选最小的。

对于特殊条件 2:(如果有一个人只能选 \(0\),那么答案一定是 \(0\),以下分类不包括有一人只能选 \(0\) 的情况):

  • 若小 L 只能选一个数
    • 若这个数是正数——小 Q 一定选最小的数(即 \(min_B\))。
    • 若这个数是负数——小 Q 一定选最大的数(即 \(max_B\))。
  • 若小 Q 只能选一个数
    • 若这个数是正数——小 L 一定选最大的数(即 \(max_A\))。
    • 若这个数是负数——小 L 一定选最小的数(即 \(min_A\))。

部分分已经在不断地带着我们往正解走了,再进行更精细的分类其实正解就出来了。

从小 L 的角度入手。

小 Q 的选择范围分为以下三种:

\[(1) \left\{ \begin{aligned} max_B \ge 0 \\ min_B \ge 0 \end{aligned} \right. ~~~~ (2) \left\{ \begin{aligned} max_B \le 0 \\ min_B \le 0 \end{aligned} \right. ~~~~ (3) \left\{ \begin{aligned} max_B \ge 0 \\ min_B \le 0 \end{aligned} \right. \]

(分别对应着只有正数、只有负数和既有正又有负)

  • 对于 \((1)\),小 L 一定会选 \(max_A\),但也得分三种情况:

\[(a) max_A < 0 ~~~~ (b) max_A > 0 ~~~~(c) max_A = 0 \]

对于 \((a)\),小 Q 会选 \(max_B\);对于 \((b)\),小 \(Q\) 会选 \(min_B\);对于 \((c)\),小 \(Q\)直接摆烂,因为无论怎样都只能得到 \(0\)

  • 对于 \((2)\),小 L 一定会选 \(min_A\),分三种情况:

\[(a) min_A < 0 ~~~~ (b) min_A > 0 ~~~~(c) min_A = 0 \]

对于 \((a)\),小 Q 会选 \(max_B\);对于 \((b)\),小 Q 会选 \(min_B\);对于 \((c)\),小 Q 会随便乱选,因为无论怎样都只能得到 \(0\)

  • 对于 \((3)\),小 L 能选 \(0\) 就一定会选 \(0\)(因为无论小 L 选什么非零数,小 Q 都能将其乘成一个负数),加入这类情况后:
    • 如果小 L 出负数,则小 Q 会出 \(max_B\),小 L 自然清楚这一点,所以他会出最大的非正数(这里将其称为 \(nmax_A\))。
    • 如果小 L 出正数,则小 Q 会出 \(min_B\),小 L 自然清楚这一点,所以他会出最小的非负数(这里将其称为 \(pmin_A\))。
    • 小 L 会使结果最大,所以答案是上述两种情况的最大值,即 \(\max(nmax_A \times max_B, pmin_A \times min_B)\)

\(6\)\(\text{ST}\) 表或线段树(此题不带修,建议用常数更小的 \(\text{ST}\) 表)维护 \(max_A, min_A, max_B, min_B, nmax_A, pmin_A\)\(O(n \log n)\) 预处理,\(O(1)\) 查询。

$Code$
#include <bits/stdc++.h>

#define MAXN 100100
#define MAXK 20

using namespace std;

const int INF = 2e9;

int n, m, q, a[MAXN], b[MAXN];
int lg[MAXN], maxa[MAXN][MAXK], mina[MAXN][MAXK], maxb[MAXN][MAXK], minb[MAXN][MAXK], nmaxa[MAXN][MAXK], pmina[MAXN][MAXK];

template<typename _T>
void read(_T &_x) {
    _x = 0;
    _T _f = 1;
    char _ch = getchar();
    while (_ch < '0' || '9' < _ch) {
        if (_ch == '-') _f = -1;
        _ch = getchar();
    }
    while ('0' <= _ch && _ch <= '9') {
        _x = (_x << 3) + (_x << 1) + (_ch & 15);
        _ch = getchar();
    }
    _x *= _f;
}

template<typename _T>
void write(_T _x) {
    if (_x < 0) {
        putchar('-');
        _x = -_x;
    }
    if (_x > 9) write(_x / 10);
    putchar('0' + _x % 10);
}

void init() {
    for (int i = 2; i <= n || i <= m; i++) lg[i] = lg[i >> 1] + 1;
    for (int i = 1; i <= n; i++) maxa[i][0] = mina[i][0] = a[i];
    for (int i = 1; i <= m; i++) maxb[i][0] = minb[i][0] = b[i];
    for (int i = 1; i <= n; i++) {
        if (a[i] == 0) nmaxa[i][0] = pmina[i][0] = 0;
        else if (a[i] < 0) nmaxa[i][0] = a[i], pmina[i][0] = INF;
        else nmaxa[i][0] = -INF, pmina[i][0] = a[i];
    }
    for (int k = 1; (1 << k) <= n; k++) {
        for (int i = 1; i + (1 << k) - 1 <= n; i++) {
            maxa[i][k] = max(maxa[i][k - 1], maxa[i + (1 << (k - 1))][k - 1]);
            mina[i][k] = min(mina[i][k - 1], mina[i + (1 << (k - 1))][k - 1]);
            nmaxa[i][k] = max(nmaxa[i][k - 1], nmaxa[i + (1 << (k - 1))][k - 1]);
            pmina[i][k] = min(pmina[i][k - 1], pmina[i + (1 << (k - 1))][k - 1]);
        }
    }
    for (int k = 1; (1 << k) <= m; k++) {
        for (int i = 1; i + (1 << k) - 1 <= m; i++) {
            maxb[i][k] = max(maxb[i][k - 1], maxb[i + (1 << (k - 1))][k - 1]);
            minb[i][k] = min(minb[i][k - 1], minb[i + (1 << (k - 1))][k - 1]);
        }
    }
}

int getmaxa(int l, int r) {
    int ln = lg[r - l + 1];
    return max(maxa[l][ln], maxa[r - (1 << ln) + 1][ln]);
}

int getmina(int l, int r) {
    int ln = lg[r - l + 1];
    return min(mina[l][ln], mina[r - (1 << ln) + 1][ln]);
}

int getmaxb(int l, int r) {
    int ln = lg[r - l + 1];
    return max(maxb[l][ln], maxb[r - (1 << ln) + 1][ln]);
}

int getminb(int l, int r) {
    int ln = lg[r - l + 1];
    return min(minb[l][ln], minb[r - (1 << ln) + 1][ln]);
}

int getnmaxa(int l, int r) {
    int ln = lg[r - l + 1];
    return max(nmaxa[l][ln], nmaxa[r - (1 << ln) + 1][ln]);
}

int getpmina(int l, int r) {
    int ln = lg[r - l + 1];
    return min(pmina[l][ln], pmina[r - (1 << ln) + 1][ln]);
}

int main() {
    read(n), read(m), read(q);
    for (int i = 1; i <= n; i++) read(a[i]);
    for (int i = 1; i <= m; i++) read(b[i]);
    init();
    while (q--) {
        int a, b, c, d;
        read(a), read(b), read(c), read(d);
        int amax = getmaxa(a, b), amin = getmina(a, b), bmax = getmaxb(c, d), bmin = getminb(c, d);
        if (bmin >= 0) {
            if (amax < 0) write(1ll * amax * bmax);
            else if (amax > 0) write(1ll * amax * bmin);
            else write(0);
        } else if (bmax <= 0) {
            if (amin < 0) write(1ll * amin * bmax);
            else if (amin > 0) write(1ll * amin * bmin);
            else write(0);
        } else {
            int nmax = getnmaxa(a, b), pmin = getpmina(a, b);
            if (nmax == -INF) write(1ll * pmin * bmin);
            else if (pmin == INF) write(1ll * nmax * bmax);
            else write(max(1ll * nmax * bmax, 1ll * pmin * bmin));
        }
        putchar('\n');
    }
    return 0;
}

\(\bold{T3}\)

题面很长,认认真真读下来后不难发现:如果把每个虫洞看作是一条无向边的话,“绝佳的反攻时刻”时 \(n\) 个点一定会构成基环森林。就着这个性质再读一遍条件,又不难发现每个据点能够“连续穿梭”时势必可以“实现反击”,此时每个点的出度都为 \(1\)

于是乎,题意简化成了:

给定 \(n\) 个点,\(m\) 条边,有如下操作:

\(1\):删除一条边。

\(2\):删除一个点的所有入边。

\(3\):恢复一条边。

\(4\):恢复一个点的所有入边。

每一次操作完成后,若每个点的出度都为 \(1\),输出 YES,否则输出 NO

对每一个点随机一个点权,然后 \(\text{Hash}\)

$Code$
#include <bits/stdc++.h>

#define MAXN 500100

using namespace std;

typedef long long ll;

const int MOD = 1e9 + 7;

int n, m, q;
int tot, head[MAXN];
ll cur, ok, w[MAXN], s[MAXN], pres[MAXN];

template<typename _T>
void read(_T &_x) {
    _x = 0;
    _T _f = 1;
    char _ch = getchar();
    while (_ch < '0' || '9' < _ch) {
        if (_ch == '-') _f = -1;
        _ch = getchar();
    }
    while ('0' <= _ch && _ch <= '9') {
        _x = (_x << 3) + (_x << 1) + (_ch & 15);
        _ch = getchar();
    }
    _x *= _f;
}

int main() {
    srand(time(0));
    read(n), read(m);
    for (int i = 1; i <= n; i++) {
        w[i] = rand() % MOD;
        ok = (ok + w[i]) % MOD;
    }
    while (m--) {
        int u, v;
        read(u), read(v);
        s[v] = (s[v] + w[u]) % MOD;
        cur = (cur + w[u]) % MOD;
    }
    for (int i = 1; i <= n; i++) pres[i] = s[i];
    read(q);
    while (q--) {
        int op, u, v;
        read(op), read(u);
        if (op == 1) {
            read(v);
            s[v] = (s[v] - w[u] + MOD) % MOD;
            cur = (cur - w[u] + MOD) % MOD;
        } else if (op == 2) {
            cur = (cur - s[u] + MOD) % MOD;
            s[u] = 0;
        } else if (op == 3) {
            read(v);
            s[v] = (s[v] + w[u]) % MOD;
            cur = (cur + w[u]) % MOD;
        } else {
            cur = (cur + pres[u] - s[u] + MOD) % MOD;
            s[u] = pres[u];
        }
        if (cur == ok) puts("YES");
        else puts("NO");
    }
    return 0;
}

\(\bold{T4}\)

\(k = 1\) 时,就是一个求两点间简单路径上点权和的问题。预处理出树上每个点 \(u\) 到根节点的权值和 \(s[u]\),若给出的起点和终点分别为 \(x, y\),则 \(ans = s[x] + s[y] - s[\text{lca}(x, y)] - s[fa[\text{lca}(x, y)]]\)。预处理 \(O(n \log n)\),查询 \(O(q \log n)\)\(k = 1\) 的情况完美解决。

接下来难点来到了 \(k = 2\)\(k = 3\)

看见最小值,还在树上,乍一眼也看不出来该用啥,大概率就是个 \(\text{DP}\)绝对不是因为前三题都没有动态规划感觉怪怪的

考虑如何设计状态。对于 \(s \to t\)\(s\)\(t\) 的简单路径)上的一点 \(u\),有哪些点会对从 \(s\) 出发到达 \(u\) 花费的最少时间做出贡献呢?很容易想到是所有满足 \(\text{dis}(u, v) \le k\) 的点 \(v\),再细想,\(v\) 应该不位于 \(u\) 以后(谁赶时间的时候还玩迂回啊),于是:

\(f_{u, i}\) 表示从起点到达与 \(u\) 距离为 \(i\) 的点花费的最少时间,\(minv_u\) 表示与 \(u\) 直接相连的点中的最小点权,\(pre\) 为从起点到终点的路径上 \(u\) 的上一个点。

\(k = 2\) 时:

\[f_{u, 0} = \min(f_{pre, 0}, f_{pre, 1}) + v_u \\ f_{u, 1} = f_{pre, 0} \]

\(k = 3\) 时:

\[f_{u, 0} = \min\{f_{pre, 0}, f_{pre, 1}, f_{pre, 2}\} + v_u \\ f_{u, 1} = \min\{f_{pre, 0}, f_{pre, 1} + minv_u\} \\ f_{u, 2} = f_{pre, 1} \]

\(k = 3\) 时为什么 \(f[pre][1]\) 也能更新 \(f[u][1]\)

\(x \to pre \to u \to v (v \in \text{son}(u))\),刚好三步,\(v\)\(u\) 的距离也恰好是 \(1\)

发现这样一来时间复杂度变成了 \(O(nq)\),显然不够我们通过此题,查询的 \(O(q)\) 是不得不带的,要想提高效率就得从转移的 \(O(n)\) 出发。注意到:我们其实并不关心过程中的任何一个 \(f_{u, i}\),只关心最后的 \(f_{t, 0}\),也就是不要中间量,只要结果,可以但我一开始的确没有想到矩阵

我们可以重定义下矩阵乘法:

对于 \(A \times B = C\),有 \(C_{i, j} = \min\{A_{i, k} + B_{k, j}\}\)

则有:

\(k = 2\) 时:

\[\begin{bmatrix} f_{pre, 0} & f_{pre, 1} \end{bmatrix} \times \begin{bmatrix} v_u & 0 \\ v_u & +\infin \\ +\infin & +\infin \end{bmatrix}= \begin{bmatrix} f_{u, 0} & f_{u, 1} \end{bmatrix} \]

\(k = 3\) 时:

\[\begin{bmatrix} f_{pre, 0} & f_{pre, 1} & f_{pre, 2} \end{bmatrix} \times \begin{bmatrix} v_u & 0 & +\infin \\ v_u & minv_u & 0 \\ v_u & +\infin & +\infin \end{bmatrix}= \begin{bmatrix} f_{u, 0} & f_{u, 1} & f_{u, 2} \end{bmatrix} \]

特别地,当 \(u = s\) 时:

\(k = 2\) 时:

\[\begin{bmatrix} 0 & 0 \end{bmatrix} \times \begin{bmatrix} v_u & +\infin \\ +\infin & +\infin \\ +\infin & +\infin \end{bmatrix}= \begin{bmatrix} f_{u, 0} & f_{u, 1} \end{bmatrix} \]

\(k = 3\) 时:

\[\begin{bmatrix} 0 & 0 & 0 \end{bmatrix} \times \begin{bmatrix} v_u & +\infin & +\infin \\ +\infin & +\infin & +\infin \\ +\infin & +\infin & +\infin \end{bmatrix}= \begin{bmatrix} f_{u, 0} & f_{u, 1} & f_{u, 2} \end{bmatrix} \]

加法和取最小值都满足结合律,那把它们并到一起,也是满足结合律的,所以我们新定义的矩阵乘法仍满足结合律。那就可以大胆对 \(s \to \text{LCA(s, t)} \to t\) 倍增,总的时间复杂度降到 \(O((n + q) k^3 \log n)\)

最后统计答案时,要把 \(s = \text{lca}(s, t)\) 单独拿出来讨论(防止多算 \(s\) 处的转移矩阵)。

$Code$
#include <bits/stdc++.h>

#define MAXN 200100

using namespace std;

typedef long long ll;

int n, q, p, w[MAXN], dep[MAXN], minv[MAXN], fa[MAXN][20];
int tot, head[MAXN];
ll s[MAXN];

struct Edge {
    int to, nxt;
} e[MAXN << 1];

struct Matrix {
    int n, m;
    ll a[3][3];

    Matrix() {
        memset(a, 0x3f, sizeof(a));
    }

    Matrix operator*(const Matrix &rhs) const {
        Matrix res;
        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                for (int k = 0; k < 3; k++) {
                    res.a[i][j] = min(res.a[i][j], a[i][k] + rhs.a[k][j]);
                }
            }
        }
        return res;
    }
} f[MAXN][20], g[MAXN][20];

template<typename _T>
void read(_T &_x) {
    _x = 0;
    _T _f = 1;
    char _ch = getchar();
    while (_ch < '0' || '9' < _ch) {
        if (_ch == '-') _f = -1;
        _ch = getchar();
    }
    while ('0' <= _ch && _ch <= '9') {
        _x = (_x << 3) + (_x << 1) + (_ch & 15);
        _ch = getchar();
    }
    _x *= _f;
}

template<typename _T>
void write(_T _x) {
    if (_x < 0) {
        putchar('-');
        _x = -_x;
    }
    if (_x > 9) write(_x / 10);
    putchar('0' + _x % 10);
}

void add(int u, int v) {
    e[++tot] = Edge{v, head[u]};
    head[u] = tot;
}

void dfs(int u) {
    for (int i = head[u], v; i; i = e[i].nxt) {
        v = e[i].to;
        if (v == fa[u][0]) continue;
        dep[v] = dep[u] + 1;
        fa[v][0] = u;
        s[v] = s[u] + w[v];
        minv[v] = w[u];
        minv[u] = min(minv[u], w[v]);
        dfs(v);
    }
}

int LCA(int u, int v) {
    if (dep[u] < dep[v]) swap(u, v);
    for (int k = 17; k >= 0; k--) if (dep[fa[u][k]] >= dep[v]) u = fa[u][k];
    if (u == v) return u;
    for (int k = 17; k >= 0; k--) if (fa[u][k] != fa[v][k]) u = fa[u][k], v = fa[v][k];
    return fa[u][0];
}

void solve1() {
    int u, v, lca, flca;
    while (q--) {
        read(u), read(v);
        lca = LCA(u, v), flca = fa[lca][0];
        write(s[u] + s[v] - s[lca] - s[flca]);
        putchar('\n');
    }
    exit(0);
}

Matrix newm(int u) {
    Matrix res;
    if (p == 2) {
        res.a[0][0] = res.a[1][0] = w[u];
        res.a[0][1] = 0;
    } else {
        res.a[0][0] = res.a[1][0] = res.a[2][0] = w[u];
        res.a[0][1] = res.a[1][2] = 0;
        res.a[1][1] = minv[u];
    }
    return res;
}

Matrix up(int u, int v) {
    Matrix res;
    res.a[0][0] = res.a[1][1] = res.a[2][2] = 0;
    if (dep[u] <= dep[v]) return res;
    for (int k = 17; k >= 0; k--) {
        if (dep[fa[u][k]] >= dep[v]) {
            res = res * f[u][k], u = fa[u][k];
        }
    }
    return res;
}

Matrix down(int u, int v) {
    Matrix res;
    res.a[0][0] = res.a[1][1] = res.a[2][2] = 0;
    if (dep[u] <= dep[v]) return res;
    for (int k = 17; k >= 0; k--) {
        if (dep[fa[u][k]] >= dep[v]) {
            res = g[u][k] * res, u = fa[u][k];
        }
    }
    return res;
}

Matrix S(int u) {
    Matrix res;
    res.a[0][0] = w[u];
    return res;
}

int main() {
    read(n), read(q), read(p);
    for (int i = 1; i <= n; i++) read(w[i]);
    for (int i = 1, u, v; i < n; i++) {
        read(u), read(v);
        add(u, v), add(v, u);
    }
    dep[1] = 1, s[1] = w[1], minv[1] = 1e9;
    dfs(1);
    for (int k = 1; k <= 17; k++) {
        for (int i = 1; i <= n; i++) {
            fa[i][k] = fa[fa[i][k - 1]][k - 1];
        }
    }
    if (p == 1) solve1();
    for (int i = 1; i <= n; i++) f[i][0] = g[i][0] = newm(i);
    for (int k = 1; k <= 17; k++) {
        for (int i = 1; i <= n; i++) {
            f[i][k] = f[i][k - 1] * f[fa[i][k - 1]][k - 1];
            g[i][k] = g[fa[i][k - 1]][k - 1] * g[i][k - 1];
        }
    }
    int u, v, lca;
    Matrix ans;
    while (q--) {
        read(u), read(v);
        lca = LCA(u, v);
        if (lca == u) ans = S(u) * down(v, lca);
        else ans = S(u) * up(fa[u][0], lca) * newm(lca) * down(v, lca);
        write(ans.a[0][0]), putchar('\n');
    }
    return 0;
}

ENDING#

出分了。。。

\(15 + 100 + 40 + 0 = 155\)

为什么心态这么不稳啊啊啊啊啊啊啊啊啊………………

作者:chy12321

出处:https://www.cnblogs.com/chy12321/p/16864664.html

版权:本作品采用「署名-非商业性使用-相同方式共享 4.0 国际」许可协议进行许可。

posted @   Chy12321  阅读(309)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示
more_horiz
keyboard_arrow_up dark_mode palette
选择主题
menu