闲话 23.3.19

闲话

好 反正是五道题(

模拟赛

T1
考虑不带修咋做,一眼线段树维护矩乘。
由于这些操作都是线性的(这显然),我们能预料到这些操作可以被矩阵描述。
平移是 \((x, y) \to (x + u, y + v)\);投影到方向 \(\bm v\) 上是点乘 \(\bm v\) 后数乘 \(\bm v\) 再除以 \(|\bm v|^2\);逆时针旋转 \(\theta\) 度是 \((x, y)\to (x\cos \theta -y \sin \theta, y \cos \theta + x \sin \theta)\)。考虑答案是 \((x, y, 1)\),我们总能通过 \(3\times 3\) 的矩阵描述这些操作。
但是现在带修了,我们不能直接复合对 \((x, y)\) 的操作:矩阵没交换律。
我场上是这么想的:对操作序列分块,块长是 \(B\),把所有询问按块离线。对每块,首先在时间维上建线段树,把对整块作修改的修改先加入,这复杂度是 \(O(nm\log n/B)\) 的。做扫描线,对每个点插入/删除对一部分的修改,这是 \(O(m\log n)\) 的。这时我们有了每个点对修改的线段树,每次必定是查询一个前缀的矩阵乘积,这个对非整块的询问是 \(O(mB\log n)\) 的。最后是覆盖整块的查询,我们大概可以做线段树合并得到 \(O(nm\log^2 n/B + m\log^2 n)\),然后查询是 \(O(mB\log n)\) 的。这样能得到复杂度是 \(O(nm\log^2 n / B + mB\log n)\),取 \(B = \sqrt{n\log n}\) 得到 \(O(m\sqrt{n}\log^{3/2}n)\) 的复杂度,大概不满。假设我们拿一个 \(O(C)\) 修改、\(O(D)\) 查询,单块内节点数是 \(O(f(n))\) 的结构维护,复杂度就会变成 \(O(nmC/B + mC + mBD + nf(n)\log n/B + mBD)\)。考虑用一个 \(O(n^{k}) - O(1)\) 的分块维护,知道这时的 \(f(n) = Bn^{1-k}\)。这样能得到 \(B = n^{(1 + k)/2}\) 最优,得到复杂度 \(O(mn^{(1 + k)/2} + n^{2-k}\log n + mn^k)\),视 \(m = \Theta(n)\) 可以得到 \(O(n^{1.5+k/2}+n^{1 + k}+n^{2-k}\log n)\)。取 \(k = 0.508\) 大致能得到最小值,在 \(10^5\) 内略优于上一种做法。我不想再分析了,这做法很难做到维护矩阵,而且题解用了性质,复杂度比我优。
题解说考虑不移动操作,而是移动坐标系。对第一种操作,可以考虑先移动坐标系,再做操作,再移动回来。假设移动坐标的矩阵是 \(D\),原操作矩阵是 \(M\),我们需要的就是先乘 \(D\),再乘 \(M\),再乘 \(D^{-1}\),也就是打乘法标记 \(DMD^{-1}\)。对第二种操作,整体翻转点可以直接构造矩阵 \(R\),但操作序列不太好维护,因此我们不妨维护 \(M, M'\) 表示正向的操作和翻转后的操作,翻转就使得 \(M\leftarrow RM'R^{-1}, \ M' \leftarrow RMR^{-1}\)
这样可以在 \(O(n\log n)\) 的复杂度内完成。

T2
不妨先考虑 \(\exists B, \ AB = A^{\mathsf T}\) 的条件,即 \(A\) 的性质。
\(A\) 的列向量组为序列 \(\langle a_i \rangle\)\(A^{\mathsf T}\) 的为 \(\langle b_i\rangle\)。考虑 \(AB = A^{\mathsf T}\) 等价于 \((a_1, a_2, \dots, a_n) B = (b_1, b_2, \dots, b_n)\),也就是说每个 \(b_i\) 都可以被 \(a_i\) 线性表示。由对称性,显然存在 \(C\) 满足 \(A^{\mathsf T}C = A\),因此每个 \(a_i\) 都可以被 \(b_i\) 线性表示。
因此可以知道,\(\exists B, \ AB = A^{\mathsf T}\) 当且仅当 \(\text{Col}(A) = \text{Col}(A^{\mathsf T})\),即 \(A\) 的行空间与列空间相同。考虑一个满足此条件的 \(A\),并记 \(r = \text{rank}(A)\)。可以知道,\(A\) 的行空间与列空间相同等价于 \(A\) 能被唯一表示为 \(CMC^{\mathsf T}\) 的形式,其中 \(C\in \mathbb F_p^{n\times r}\)\(\text{Col}(A)\) 的一组基,\(M \in \mathbb F_p^{r\times r}\text{ s.t. } \text{rank}(M) = r\)。可以感性理解,证明就考虑这组基的唯一性和表示法。
因此我们可以对每个 \(r\),计数满足条件的 \(A = CMC^{\mathsf T}\) 和对应的 \(B\)
先考虑前一个部分,这显然需要 \(\text{q-analog}\) 的知识。
计数 \(M\) 可以归约到同态计数,答案就是

\[GL(r) = \prod_{i = 0}^{r - 1} (p^r - p^i) = p^{r\times r} \prod_{i = 1}^{r} (1 - p^{-i}) \]

可以递推解决。
计数 \(C\)经典结论,答案就是 \(\begin{bmatrix} n\\ r\end{bmatrix}_p\)
再考虑后一个部分。我们有 \(CMC^{\mathsf T}B = CM^{\mathsf T}C^{\mathsf T}\),稍作化简可以得到 \(C^{\mathsf T}B = M^{-1}M^{\mathsf T}C^{\mathsf T}\)。右边对一个 \(A\) 是唯一的,左边可以发现 \(M\) 能确定 \(r\) 行的值,剩下的值随意。因此对一个满足条件的 \(A\)\(B\) 的计数(即 \(M(A)\)\(p^{n(n - r)}\)。因此可以知道 \(M_{\log}(A) = n(n - r) + 1\)
对一个 \(n\),答案即为

\[\sum_{r = 0}^n \begin{bmatrix} n\\ r\end{bmatrix}_p GL(r) \left(n(n - r) + 1\right) = [n]_p! \sum_{i + j = n} \frac{GL(i)}{[i]_p!} \frac{nj + 1}{[j]_p!} \]

做个乘法即可。总时间复杂度 \(O(k\log k)\)

T3
比上面的简单。考虑贪心。
如果不考虑加边,我们只需要用 pqueue 而不是 queue 维护拓扑排序过程。设这个堆是 \(q\)。当某个时间堆里有多于一个元素并加边次数没用完时,我们希望通过加边减缓最小元素出堆的时间。这个等于确定了一个终点,找一个起点。我们再拿一个大根堆 \(q'\),把这些被延后的点记录进 \(q'\) 中。我们可以让这些点一直待定,直到 \(q\) 里没有元素了。此时为了保证图是 DAG,我们必须使得 \(q'\) 中的某个点可以入队。于是我们取出 \(q'\) 中最大的一个,把它上面加的边的起点设置为当前拓扑序列的最后一个点,表示此时刚好这个点可以入队。
复杂度 \(O(n\log n)\)

杂题

CF1804G

在一个带宽为 \(b\) 字节每毫秒的网络中,有 \(n\) 个用户需要传输数据。

\(i\) 个用户会在第 \(s_i\) 毫秒到第 \(t_i\) 毫秒(包含端点)使用该网络,其初始传输速度为 \(d_i\)。也就是说,这名用户在 \(s_i\) 毫秒开始使用该网络,此时其传输速度为 \(d_i\)。随后传输速度会遵循以下程序而改变。

假设在第 \(x\) 毫秒有 \(m\) 个用户使用该网络,第 \(i\) 个用户当前的传输速度为 \(t_i \ge 0\)

  1. \(m = 0\),此时没有用户在使用网络。什么都不会发生。
  2. 如果所有 \(t_i\) 的和 \(\le b\),每个使用网络的用户都能成功传输数据(第 \(i\) 个用户传输了 \(t_i\) 字节)。在这之后,每个用户的传输速度(即每个 \(t_i\))都会增加 \(1\)
  3. 如果所有 \(t_i\) 的和 \(> b\),会发生网络拥塞。没有用户在该毫秒内能传输数据。在这之后,每个用户的传输速度都会除以二(即每个 \(t_i\) 被替换为 \(\left\lfloor \frac{t_i}{2}\right\rfloor\))。

给定 \(n, b\) 和每个 \(s_i, t_i, d_i\),请计算所有用户总共可以传输的数据量。

\(1\le n\le 2\times 10^5, \ 1\le b, d_i \le 10^9, \ 1\le s_i\le t_i\le 10^9\)

记值域为 \(V\)

首先可以发现,如果没有元素的变化,我们总能通过 \(O(\log V)\) 次 3. 操作将所有值变为相同的值,这启发我们将加一和除二分开进行。下面先不考虑加入/删除元素。也就是说,我们视 \(\forall i,j, \ s_i = s_j, t_i = t_j\)

根据上面的讨论,我们不难发现在所有值变为相同元素前,3. 操作的总次数是 \(O(n\log V)\) 的。同时,不难快速计算连续的 2. 操作的次数和对答案的贡献。如果我们知道了 \(m\)\(s = \sum t_i\),我们就能够 \(O(1)\) 地计算出加一的次数,即 \(c = \frac{b - s + m}{m}\),从而可以计算这些 1. 操作的贡献,即 \(sc + m\frac{c(c - 1)}{2}\)。这样总时间复杂度只取决于 3. 操作。

我们需要为每个值加 \(k > 0\)、为每个值除二,在这过程中合并相同值,维护值的个数 \(m\) 和值的和 \(s\)
考虑朴素维护(每次 \(O(m)\) 地遍历所有值做操作)的复杂度。设最开始不同的 \(t_i\) 共有 \(v\) 个。假设 \(\forall i,\ t_i < t_{i + 1}\),我们能知道 \(t_i\) 会在 \(O(\log(t_{i + 1} - t_i))\) 次操作内与 \(t_{i + 1}\) 合并,这样操作次数是均摊 \(O\left(v + \sum_{i = 1}^{v - 1} \log(t_{i + 1} - t_i)\right)\) 的。因为首先需要 \(O(v)\) 的复杂度做加法,随后不断除 \(2\),总复杂度通过均摊可以得到。
在具体实现上,可以通过并查集维护代表元的方式得到 \(O(n\log V \alpha (n))\) 的复杂度,或是线段树朴素递归的方式得到 \(O(n\log V\log n)\) 的复杂度。这两种作法都支持快速加入或删除元素。不难证明加入新元素不会影响总时间复杂度。

这样我们只需要考虑所有值都相同且无值增删时的情况了,而这是容易处理的。我们取得两次除 \(2\) 后的情况,若这两次的和、最大值、最小值分别相同,且最大值与最小值相差不大时(注意这里不是必须相同),我们判定之后的操作存在周期。我们只需要记录答案的差值、时间的差值,就可以快速计算到下次值变化前有多少个周期。得到这之后就可以快速跳到最后一个周期了。

总时间复杂度 \(O(n\log n\log V)\)(线段树)。

Submission.

P6931

给定一个 \(r \times c\) 的平面,在上面摆有一些箱子。我们可以得到他的三视图。设第 \(i\) 行第 \(j\) 列的箱子数是 \(a_{i, j}\)

你可以拿走一些箱子,和重新排列这些箱子的位置,你想知道,最多能拿走多少个箱子,使得这些箱子重新排列后正视图,俯视图,左视图不变?

\(1 \le r,c \le 100\),平面上每一个位置的箱子个数在 \([0,10^9]\) 内。

三视图,每个有箱子的位置至少留一个,我们不妨先全拿走,然后找一些位置放回去,使得每行每列最大值相同。
我们不妨找到每行每列的最大值,以及行列中有箱子的所有位置,这些位置可以放行列最大值。
于是行列建 \(O(r + c)\) 个点,问题被转化成二分图带权最小匹配。这也可以被转化成最大流。
总时间复杂度 \(O(\text{Dinic}(r + c, rc))\)

code
#include <bits/stdc++.h>
using namespace std;
#define int long long
using pii = pair<int,int>; using vi = vector<int>; using vp = vector<pii>; using ll = long long; 
using ull = unsigned long long; using db = double; using ld = long double;
template<typename T1, typename T2> T1 max(T1 a, T2 b) { return a > b ? a : b; }
template<typename T1, typename T2> T1 min(T1 a, T2 b) { return a < b ? a : b; }
#define multi int T; cin >> T; while ( T -- )
#define timer cerr << 1. * clock() / CLOCKS_PER_SEC << '\n';
#define iot ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
#define file(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define rep(i,s,t) for (register int i = (s), i##_ = (t) + 1; i < i##_; ++ i)
#define pre(i,s,t) for (register int i = (s), i##_ = (t) - 1; i > i##_; -- i)
#define eb emplace_back
#define pb pop_back
const int N = 1e3 + 10;
int s, t, M, r, c, ans, a[N][N], mxr[N], mxc[N];

int head[2][N], mlc = 1;
struct ep {
    int to, next, flow;
} e[N * N];
void adde(int u, int v, int f, bool __same = false) {
    e[++ mlc] = { v, head[0][u], f };
    head[0][u] = mlc;
    e[++ mlc] = { u, head[0][v], __same ? f : 0 };
    head[0][v] = mlc; 
}

int dep[N]; 
bool bfs(int s, int t) {
    rep(i,1,M) dep[i] = 0, head[1][i] = head[0][i];
    queue<int> que;
    que.push(s); dep[s] = 1;
    while (que.size()) {
        int u = que.front(); que.pop();
        for (int i = head[0][u], v; i; i = e[i].next) {
            v = e[i].to;
            if (e[i].flow and !dep[v]) {
                dep[v] = dep[u] + 1;
                if (v == t) return 1;
                que.push(v);
            }
        }
    } return dep[t];
}

int dfs(int u, int in, int t) {
    if (u == t or in <= 0) return in;
    int out = 0;
    for (int &i = head[1][u], v; i; i = e[i].next) {
        v = e[i].to;
        if (e[i].flow and dep[v] == dep[u] + 1) {
            int res = dfs(v, min(in, e[i].flow), t);
            in -= res, out += res;
            e[i].flow -= res, e[i ^ 1].flow += res;
            if (in <= 0) break;
        }
    }
	if (out <= 0) dep[u] = 0;
	return out;
}

int Dinic(int s, int t) {
    int ret = 0, fl;
    while (bfs(s, t)) while (fl = dfs(s, 1e18, t)) ret += fl;
    return ret;
}

signed main() {
	cin >> r >> c;
	rep(i,1,r) rep(j,1,c) cin >> a[i][j], ans += max(0, a[i][j] - 1), mxr[i] = max(mxr[i], a[i][j]), mxc[j] = max(mxc[j], a[i][j]);
	rep(i,1,r) if (mxr[i]) ans -= mxr[i] - 1;
	rep(i,1,c) if (mxc[i]) ans -= mxc[i] - 1;
	M = r + c; s = ++ M, t = ++ M;
	rep(i,1,r) rep(j,1,c) if (mxr[i] == mxc[j] and a[i][j]) adde(i, j + r, 1e15);
	rep(i,1,r) if (mxr[i]) adde(s, i, mxr[i] - 1);
	rep(i,1,c) if (mxc[i]) adde(i + r, t, mxc[i] - 1);
	cout << ans + Dinic(s, t) << '\n';
}
posted @ 2023-03-19 15:42  joke3579  阅读(93)  评论(0编辑  收藏  举报