2023 年牛客多校第四场题解

A Bobo String Construction

题意:给定一个 01 01 01 字符串 t t t,构造一个长度为 n n n 01 01 01 s s s,使得 t t t c o n c a t ( t , s , t ) {\rm concat}(t, s, t) concat(t,s,t) 中仅出现两次。多测, 1 ≤ T ≤ 1 0 3 1 \le T \le 10^3 1T103 1 ≤ n , ∣ t ∣ ≤ 1 0 3 1 \le n,|t| \le 10^3 1n,t103

解法:结论是全 0 0 0 或全 1 1 1 串一定可行。

首先如果 t t t 就是全 0 0 0 或全 1 1 1,那显然构造全 1 1 1 或全 0 0 0 串一定可行。

如果 t t t 01 01 01 混杂,考虑以下两种情况:

  1. 首先 s s s 串内部肯定不会出现 t t t
  2. 考虑 c o n c a t ( t , s ) {\rm concat}(t,s) concat(t,s) c o n c a t ( s , t ) {\rm concat}(s,t) concat(s,t) 部分。显然只需要考虑 t t t 的 border(最长公共前后缀)和 s s s 的拼接部分即可。如果 border 部分 01 01 01 混杂那显然交叠部分不会出现。如果 border 只有 0 0 0,那就构造全 1 1 1,反之亦然。则 s s s 的交叠部分末端(可能出现匹配的首段是 t t t 的尾端 border)一定无法出现 t t t 的 border,也就不会出现匹配。

所以枚举到底是全 0 0 0 还是全 1 1 1,然后使用 KMP 算法计算 c o n c a t ( t + s + t ) {\rm concat}(t+s+t) concat(t+s+t) t t t 是否只出现两次即可。复杂度 O ( T ( n + ∣ t ∣ ) ) \mathcal O(T(n+|t|)) O(T(n+t))

#include <bits/stdc++.h>
using namespace std;
// KMP template
class KMP
{vector<int> nx;string b;public:KMP(string b){this->b = b;int n = b.length();int j = 0;nx.resize(n);for (int i = 1; i < n; i++){while (j > 0 && b[i] != b[j])j = nx[j - 1];if (b[i] == b[j])j++;nx[i] = j;}}int find(string a) // a中出现多少次b{int n = b.length(), m = a.length();int j = 0;int ans = 0;for (int i = 0; i < m; i++){while (j > 0 && a[i] != b[j])j = nx[j - 1];if (a[i] == b[j])j++;if (j == n){ans++;j = nx[j - 1];}}return ans;}
};
void Solve()
{int n;string t;cin >> n >> t;string s0, s1;for (int i = 0; i < n; i++){s0 += "0";s1 += "1";}KMP solve(t);if (solve.find(t + s0 + t) == 2)cout << s0 << "\n";else if (solve.find(t + s1 + t) == 2)cout << s1 << "\n";elsecout << "-1\n";
}
int main()
{cin.tie(0)->sync_with_stdio(0);cin.exceptions(cin.failbit);cin.tie(NULL);cout.tie(NULL);int t;cin >> t;while (t--)Solve();return 0;
}

F Election of the King

题意:给定长度为 n n n 的数列 { a } i = 1 n \{a\}_{i=1}^n {a}i=1n,最开始每个数字都存在。持续进行 n − 1 n-1 n1 轮下述操作:

  1. 当前剩下来的每个数,选择距离它最远(绝对值最大)的数进行投票,如果最远距离相等选择大的。
  2. 当前被投票数最多的数在本轮删掉,平票则选择最大的数字删掉。

问最后是哪个数字留下来。 1 ≤ n ≤ 1 0 6 1 \le n \le 10^6 1n106 1 ≤ a i ≤ 1 0 9 1 \le a_i \le 10^9 1ai109

解法:考虑维护数列的中位数,并观察它的投票情况。因为如果中位数投最大,它左侧也一定投最大;中位数投最小,它右侧也一定投最小。因而哪怕是两侧投票数势均力敌也是由中位数定胜负。因而时刻维持中位数投票情况以决定淘汰的数字是谁,同时同步移动中位数即可。整体复杂度为 O ( n log ⁡ n + n ) \mathcal O(n \log n+n) O(nlogn+n)

#include <bits/stdc++.h>
using namespace std;
const int N = 1000000;
pair<int, int> a[N + 5];
int main()
{int n;scanf("%d", &n);for (int i = 1; i <= n; i++){scanf("%d", &a[i].first);a[i].second = i;}sort(a + 1, a + n + 1);int l = 1, r = n;// [l, r] 区间表示存活的数字for (int i = n, j = (n + 1) / 2; i >= 2; i--){int midl = (a[r].first - a[j].first >= a[j].first - a[l].first);if (i % 2 == 0) // 偶数要考虑中位数相邻两个{int midr = (a[r].first - a[j + 1].first >= a[j + 1].first - a[l].first);if (midl || midr) // 票死大的r--;else{l++;j++;}}else{if (!midl) // 票死小的l++;else{r--;j--;}}}printf("%d", a[l].second);return 0;
}

G Famished Felbat

题意:给定长度为 n n n 的数列 { a } i = 1 n \{a\}_{i=1}^n {a}i=1n,和长度为 m m m 的数列 { b } i = 1 m \{b\}_{i=1}^m {b}i=1m。第 i i i 轮从 { b } \{b\} {b} 数列中任意选择一个数字 b j b_j bj,然后执行 a i ← a i + b j a_i \leftarrow a_i+b_j aiai+bj,然后将 b j b_j bj { b } \{b\} {b} 数列中删去。 n n n 轮操作后求 ∑ i = 1 n f ( a i ) \displaystyle \sum_{i=1}^n f(a_i) i=1nf(ai) 的期望,其中:
f ( x ) = 1 L ∑ i = 1 L ⌈ x i ⌉ f(x)=\dfrac{1}{L}\sum_{i=1}^L \left \lceil \dfrac{x}{i} \right \rceil f(x)=L1i=1Lix
L L L 为一已知常数。 1 ≤ n ≤ m ≤ 1 0 3 1 \le n \le m \le 10^3 1nm103 1 ≤ L , a i , b j ≤ 2 × 1 0 9 1 \le L,a_i,b_j \le 2\times 10^9 1L,ai,bj2×109

解法:首先由期望的线性性,每个 b i b_i bi 都会等概率加到每个 a j a_j aj 上。同时由 ⌈ x i ⌉ = ⌊ x + i − 1 i ⌋ = ⌊ x − 1 i ⌋ + 1 \left \lceil \dfrac{x}{i} \right \rceil=\left \lfloor \dfrac{x+i-1}{i} \right \rfloor=\left \lfloor \dfrac{x-1}{i} \right \rfloor+1 ix=ix+i1=ix1+1,将上取整转化到常用的下取整。因而本质是求:
n + 1 m L ∑ k = 1 L ∑ i = 1 n ∑ j = 1 m ⌊ a i + b j − 1 k ⌋ n+\dfrac{1}{mL}\sum_{k=1}^L \sum_{i=1}^n \sum_{j=1}^m \left \lfloor \dfrac{a_i+b_j-1}{k}\right \rfloor n+mL1k=1Li=1nj=1mkai+bj1
仅考虑求和部分式子的计算,下面所有的枚举都是建立在 L L L 充分大的情况,严格的式子都需要对 L L L min ⁡ \min min。首先最朴素的想法是进行整除分块,对每个 a i + b j a_i+b_j ai+bj 进行整除分块,但这样的时间复杂度为 O ( n m L ) \mathcal O\left(nm \sqrt{L}\right) O(nmL ),显然不能通过。这时可以注意到一个性质:
⌊ x + y i ⌋ = ⌊ x i ⌋ + ⌊ y i ⌋ + [ x m o d i + y m o d i ≥ i ] \left \lfloor \dfrac{x+y}{i}\right \rfloor=\left \lfloor \dfrac{x}{i}\right \rfloor+\left \lfloor \dfrac{y}{i}\right \rfloor+[x \bmod i+y \bmod i \ge i] ix+y=ix+iy+[xmodi+ymodii]
即,两个被加数本身整除的部分,再检查余数之和是否能够再凑一个 i i i。那么对于 k k k 比较小的情况,显然就可以枚举 k k k,然后先单独计算完 m ∑ k = 1 B ⌊ a i k ⌋ \displaystyle m\sum_{k=1}^B\left \lfloor \dfrac{a_i}{k}\right \rfloor mk=1Bkai n ∑ k = 1 B ⌊ b j k ⌋ \displaystyle n\sum_{k=1}^B\left \lfloor \dfrac{b_j}{k}\right \rfloor nk=1Bkbj,以及各自的余数,再通过枚举每个 b j b_j bj 的余数,检查 { a } \{a\} {a} 中余数大于等于 k − b j m o d k k-b_j \bmod k kbjmodk 的个数有多少即可。这样这部分复杂度就是 O ( B ( n + m ) + B m log ⁡ n ) \mathcal O\left(B(n+m)+Bm \log n\right) O(B(n+m)+Bmlogn)

考虑如果当 k k k 很大会怎么样。这时由于枚举的量太大,显然无法承受。但是结合整除分块的性质——在根号以下,自变量变化小,但值域变化大;在根号以上,自变量变化大,但值域变化小。因而对于 k k k 大的情况,不难考虑通过枚举整除得到的值有多少自变量区间对应来求解。因而有 k k k 较大( k ≥ B k \ge B kB B B B 为阈值)部分的转化求和式:
∑ i = 1 ⌊ L B ⌋ max ⁡ ( 0 , min ⁡ ( ⌊ x i ⌋ , L ) − B ) \sum_{i=1}^{\left \lfloor \frac{L}{B}\right \rfloor} \max\left(0, \min\left(\left \lfloor \dfrac{x}{i}\right \rfloor , L\right)-B\right) i=1BLmax(0,min(ix,L)B)
其中 i i i 是枚举的整除值, − B -B B 操作表示挖去自变量较小的部分的贡献,上限对 L L L min ⁡ \min min 以限制分母的范围。

考虑把 min ⁡ \min min 函数去掉以快速计算,那么问题转化为 a i + b j ≥ i L a_i+b_j \ge iL ai+bjiL 的二维偏序问题,这个可以用树状数组快速解决。这部分复杂度为 O ( ⌊ L B ⌋ m log ⁡ n ) O\left(\left \lfloor \dfrac{L}{B} \right \rfloor m \log n \right) O(BLmlogn)

考虑取一个合适的阈值以平衡二者,因而取 B = L B=\sqrt{L} B=L ,最终复杂度为 O ( L m log ⁡ n ) O\left(\sqrt{L} m \log n\right) O(L mlogn)

H Merge the squares!

题意:给定 n × n n\times n n×n 1 × 1 1\times 1 1×1 组成的正方形,每次可以合并相邻不超过 50 50 50 个正方形变成一个大正方形。问如何通过合并得到一个大的 n × n n\times n n×n 的大正方形,不限次数。 1 ≤ n ≤ 1 0 3 1\le n \le 10^3 1n103

解法:考虑 7 2 ≤ 50 7^2 \le 50 7250,所以如果边长 x x x [ 2 , 7 ] [2,7] [2,7] 的倍数,可以考虑直接先拆分成 d × d d\times d d×d x d × x d \dfrac{x}{d}\times \dfrac{x}{d} dx×dx 个正方形求解。

最棘手的问题在于大质数。显然质数不能按照这种乘除法的倍数拆分,因而考虑加减法。注意到完全平方和公式: ( a + b ) 2 = a 2 + 2 a b + b 2 (a+b)^2=a^2+2ab+b^2 (a+b)2=a2+2ab+b2,构造下面的图形:

在这里插入图片描述

a × a a\times a a×a b × b b\times b b×b 的正方形,和两个 a × b a\times b a×b 的矩形。正方形可以递归下去构造,考虑矩形如何尽可能少的构造。

不妨令 a > b a >b a>b,一个贪心的想法是,每次构造一个 b × b b\times b b×b 的正方形,然后留下一个 ( a − b , b ) (a-b,b) (ab,b) 的矩形递归下去构造,即类似辗转相减法:

在这里插入图片描述

每次我们都找了一个最大的正方形,这样做整体个数不会太劣。考虑它会拆分到多少个正方形: ⌊ a b ⌋ \left \lfloor \dfrac{a}{b} \right \rfloor ba b × b b\times b b×b 的正方形(横向放置),然后再加上 ( a m o d b , b ) (a \bmod b,b) (amodb,b) 的答案。因而可以用欧几里得算法求得它的答案:

int gcd(int x, int y)
{if (x == y)return 1; // 正方形if (x < y)swap(x, y);return x / y + gcd(x % y, y); // 先横向拆分,再递归到子矩形中
}

因而回到整体大正方形拆分,可以考虑枚举这样的 a a a,求出这样拆分的矩形 ( a , x − a ) (a,x-a) (a,xa) 需要包含多少个小正方形,如果不超过 24 24 24 个就可以视为一个合法的拆分。这是因为 24 × 2 + 2 = 50 24\times 2+2=50 24×2+2=50 a × a a\times a a×a b × b b\times b b×b 的正方形视为一个,剩下的 48 48 48 个均分给两个矩形构造。

#include <bits/stdc++.h>
#define fp(i, a, b) for (int i = a, i##_ = b; i <= i##_; ++i)
#define fd(i, a, b) for (int i = a, i##_ = b; i >= i##_; --i)using namespace std;
using ll = long long;
const int N = 1e3 + 5;
int n, f[N];
vector<array<int, 3>> ans;
int check(int a, int b) {if (!b) return a <= 7;int cnt = 1, c;while (b) {cnt += a / b;c = a % b, a = b, b = c;}return cnt <= 25;
}
void dfs(int, int, int);
void calcC(int, int, int, int);
void calcR(int x, int y, int r, int c) { // c = a * r + bif (r <= 1) return;int a = c / r;fp(i, 0, a - 1) dfs(x, y + i * r, r);calcC(x, y + a * r, r, c % r);
}
void calcC(int x, int y, int r, int c) { // r = a * c + bif (c <= 1) return;int a = r / c;fp(i, 0, a - 1) dfs(x + i * c, y, c);calcR(x + a * c, y, r % c, c);
}
void dfs(int x, int y, int k) {if (k == 1) return;ans.push_back({x, y, k});// printf("%d %d %d\n", x, y, k);if (!f[k]) return;int a = k - f[k], b = f[k];calcR(x + a, y, b, a), calcC(x, y + a, a, b);dfs(x, y, a), dfs(x + a, y + a, b);
}
void Solve() {scanf("%d", &n);// freopen("s.out", "w", stdout);// printf("%d\n", n);memset(f, -1, sizeof f);f[1] = 0;fp(i, 2, n) {fp(j, 0, i / 2) {if (check(i - j, j)) {f[i] = j;break;}}}dfs(1, 1, n);printf("%llu\n", ans.size());reverse(ans.begin(), ans.end());for (auto [x, y, k] : ans) printf("%d %d %d\n", x, y, k);
}
int main() {int t = 1;while (t--) Solve();return 0;
}

I Portal 3

题意: n n n 个点的有向图,给定其邻接矩阵 G G G。现在沿着一条长度为 k k k 的路径 { v } i = 1 k \{v\}_{i=1}^k {v}i=1k(给定 k k k 个路径点依次到达),并可以合并两个点 u , v u,v u,v G u , v = G v , u = 0 G_{u,v}=G_{v,u}=0 Gu,v=Gv,u=0),问合并后最短路径长。 1 ≤ n ≤ 500 1 \le n \le 500 1n500 0 ≤ G i , j ≤ 1 0 9 0\le G_{i,j} \le 10^9 0Gi,j109 1 ≤ k ≤ 1 0 6 1 \le k \le 10^6 1k106

解法:首先 Floyd 跑出任意两点之间的最短路 { d } ( i , j ) = ( 1 , 1 ) ( n , n ) \{d\}_{(i,j)=(1,1)}^{(n,n)} {d}(i,j)=(1,1)(n,n)。然后路径本身可以转化到统计经过两点 ( s , t ) (s,t) (s,t) 的次数 c ( s , t ) c(s,t) c(s,t)。考虑合并 u , v u,v u,v 两个点会发生什么:显然有些 ( s , t ) (s,t) (s,t) 会考虑绕道 ( u , v ) (u,v) (u,v) 以拉近最短路。由于不指定 u , v u,v u,v 顺序,因而可以认为一定是 s → u → v → t s \to u \to v \to t suvt。则绕道后会节省(贡献) d ( s , t ) − d ( s , u ) − d ( v , t ) d(s,t)-d(s,u)-d(v,t) d(s,t)d(s,u)d(v,t)。因而一个简易的暴力算法流程如下:

long long maxSaved = 0;
for (int u = 1; u <= n; u++)for (int v = 1; v <= n; v++){long long curSaved = 0;for (int s = 1; s <= n; s++)for (int t = 1; t <= n; t++)curSaved += max(0ll, c[s][t] * (d[s][t] - d[s][u] - d[v][t]));maxSaved = max(maxSaved, curSaved);}

即固定枚举是合并哪两个点,然后考虑路径上每一对 ( s , t ) (s,t) (s,t) 对这一对 ( u , v ) (u,v) (u,v) 的贡献。但是这样计算复杂度是 O ( n 4 ) O(n^4) O(n4)。下面给出两种做法:

O ( n 3 log ⁡ n ) O(n^3 \log n) O(n3logn)

考虑首先枚举 u , t u,t u,t,这时可以首先枚举所有的 s s s,固定 d ( s , t ) − d ( s , u ) d(s,t)-d(s,u) d(s,t)d(s,u)。对 s s s d ( s , t ) − d ( s , u ) d(s,t)-d(s,u) d(s,t)d(s,u) 项排序。当按排序后 s s s 的顺序枚举时, d ( s , t ) − d ( s , u ) d(s,t)-d(s,u) d(s,t)d(s,u) 项单增,这时如果 v v v d ( v , t ) d(v,t) d(v,t) 单增的顺序排列,就可以考虑双指针去快速找到每个 s s s 下贡献最大的 v v v 是什么。复杂度 O ( n 3 log ⁡ n + n 3 + n 2 log ⁡ n + k ) \mathcal O(n^3 \log n+n^3+n^2 \log n+k) O(n3logn+n3+n2logn+k)

#include <bits/stdc++.h>
#define fp(i, a, b) for (int i = a, i##_ = b; i <= i##_; ++i)using namespace std;
using ll = long long;
const int N = 505;
int n, k, d[N][N], c[N][N];
ll ans, len, w[N][N];
vector<pair<int, int>> val, nv[N];
void Solve() {scanf("%d%d", &n, &k);fp(i, 1, n) fp(j, 1, n) scanf("%d", d[i] + j);fp(k, 1, n) fp(i, 1, n) fp(j, 1, n)d[i][j] = min(d[i][j], d[i][k] + d[k][j]);{int u, v;scanf("%d", &u);for (k--; k--;) scanf("%d", &v), ++c[u][v], len += d[u][v], u = v;}fp(t, 1, n) {fp(v, 1, n) nv[t].push_back({d[v][t], v});sort(nv[t].begin(), nv[t].end());}fp(u, 1, n) fp(t, 1, n) {val.clear();fp(s, 1, n) if (c[s][t] && d[s][t] > d[s][u])val.push_back({d[s][t] - d[s][u], c[s][t]});sort(val.begin(), val.end());ll tot = 0, cnt = 0, i = 0;for (auto [d, c] : val) tot += (ll)d * c, cnt += c;for (auto [d, v] : nv[t]) {while (i < val.size() && d >= val[i].first)tot -= (ll)val[i].first * val[i].second, cnt -= val[i].second, ++i;w[u][v] += tot - cnt * d;}}ans = len;fp(u, 1, n) fp(v, u, n) ans = min(ans, len - w[u][v] - w[v][u]);printf("%lld\n", ans);
}
int main() {int t = 1;while (t--) Solve();return 0;
}

O ( n 3 ) O(n^3) O(n3)

转变维护思路。考虑维护一个 v v v 数组,其中第 i i i 项表示当前要合并的点是 ( i , j ) , j ∈ [ 1 , v ] (i,j),j \in [1,v] (i,j),j[1,v] 时整个经过路径最大缩短量。因而这个时候可以考虑枚举每一对 ( s , t ) (s,t) (s,t),考虑这一对 ( s , t ) (s,t) (s,t) 会对这个数组产生什么影响。下面固定 ( s , t ) (s,t) (s,t)

观察 d ( s , t ) − d ( s , u ) − d ( v , t ) d(s,t)-d(s,u)-d(v,t) d(s,t)d(s,u)d(v,t),这时第一项已经固定。再枚举 u u u,不难注意到 d ( s , t ) − d ( s , u ) d(s,t)-d(s,u) d(s,t)d(s,u) 都已经确定,这时满足 d ( v , t ) ≤ d ( s , t ) − d ( s , u ) d(v,t) \le d(s,t)-d(s,u) d(v,t)d(s,t)d(s,u) 都会更新。因而可以考虑将所有的 v v v d ( v , t ) d(v,t) d(v,t) 顺序排列,这时按 d ( s , u ) d(s,u) d(s,u) 递增的顺序枚举 u u u 的时候,更新的 v v v 就是连续的一段,可以考虑差分和前缀和维护。等到 u u u 一轮更新完,再对 v v v 恢复顺序。这样复杂度为 O ( n 3 + 2 n 2 log ⁡ n + k ) \mathcal O(n^3 +2n^2 \log n+k) O(n3+2n2logn+k)

J Qu’est-ce Que C’est?

题意:给定长度为 n n n 的数列 { a } i = 1 n \{a\}_{i=1}^n {a}i=1n,要求每个数都在 [ − m , m ] [-m,m] [m,m] 范围,且任意长度大于等于 2 2 2 的区间和都大于等于 0 0 0,问方案数。 1 ≤ n , m ≤ 5 × 1 0 3 1 \le n,m \le 5\times 10^3 1n,m5×103

解法:下面给出两种 dp 状态设计。

法一

考虑 f i , j f_{i,j} fi,j 表示填了 i i i 个数字,当前最小后缀和为 j j j 的方案数。显然 j ∈ [ − m , m ] j \in [-m,m] j[m,m]

维护转移:

  1. 填入正数,此时 j ≥ 0 j \ge 0 j0 f i , j ← ∑ k = − j m f i − 1 , k \displaystyle f_{i,j} \leftarrow \sum_{k=-j}^m f_{i-1,k} fi,jk=jmfi1,k,即填入一个数字使得这一位和上一位加起来得大于等于 0 0 0
  2. 填入一个负数。枚举填了什么数字 j j j,这时上一位必须满足最小后缀和得大于等于 − j -j j,否则拼接起来会小于 0 0 0。因而 f i , j ← ∑ k = − j m f i − 1 , k \displaystyle f_{i,j} \leftarrow \sum_{k=-j}^m f_{i-1,k} fi,jk=jmfi1,k
#include <bits/stdc++.h>
#define fp(i, a, b) for (int i = a, i##_ = b; i <= i##_; ++i)
#define fd(i, a, b) for (int i = a, i##_ = b; i >= i##_; --i)using namespace std;
using ll = long long;
const int N = 5e3 + 5, P = 998244353;
int n, m, f[2][2 * N], suf[2 * N];
void Solve() {scanf("%d%d", &n, &m);int q = 0, ans = 0;fp(i, -m, m) f[0][N + i] = 1;fp(i, 2, n) {q ^= 1;fd(x, N + m, N -m) suf[x] = (suf[x + 1] + f[q ^ 1][x]) % P;fp(x, 0, m) f[q][N + x] = suf[N - m + x];fp(x, 1, m) f[q][N - x] = suf[N + x];}fp(i, -m, m) ans = (ans + f[q][N + i]) % P;printf("%d\n", ans);
}
int main() {int t = 1;while (t--) Solve();return 0;
}

整体复杂度 O ( n 2 ) \mathcal O(n^2) O(n2)

法二

除了最后一个数字,其余的负数一定是可以和非负数绑定的。例如,考虑如下的正负数列可以被划分为:

负正/正/正/负正/负正/正/正/正/负正/

将负数和后面紧邻的正数绑定成为一个完整块,一起填充。考虑 f i , j f_{i,j} fi,j 表示前 i i i 个数,填的一个完整块的和为 j j j 的方案数。考虑如下几种情况的转移:

  • 当前填非负数。 f i , j ← ∑ k = 0 m f i − 1 , k \displaystyle f_{i,j}\leftarrow \sum_{k=0}^m f_{i-1,k} fi,jk=0mfi1,k
  • 当前准备带负数的完整块。 f i , j ← ∑ k = 0 m ∑ l = − k − 1 [ 1 ≤ j − l ≤ m ] f i − 2 , k \displaystyle f_{i,j} \leftarrow \sum_{k=0}^m \sum_{l=-k}^{-1}[1 \le j-l \le m] f_{i-2,k} fi,jk=0ml=k1[1jlm]fi2,k,即 l l l 枚举负数范围为 [ − k , − 1 ] [-k,-1] [k,1],正数需要和满足 j j j 条件下,仍然在 [ 0 , m ] [0,m] [0,m] 范围。因而有转移 f i , j ← ∑ k = 0 m min ⁡ ( k , j − m ) f i − 2 , k \displaystyle f_{i,j} \leftarrow \sum_{k=0}^m \min(k,j-m)f_{i-2,k} fi,jk=0mmin(k,jm)fi2,k

基于这些转移,可以写出这样的暴力代码:

#include <bitsdc++.h>
using namespace std;
const int N = 5000, P = 998244353;
int f[N + 5][N + 5], g[N + 5][N + 5];
int main()
{int n, m;scanf("%d%d", &n, &m);f[0][0] = 1;for (int i = 0; i <= m; i++)f[1][i] = 1;for (int i = 0; i <= m; i++){for (int j = 0; j <= m; j++)f[2][i] = (f[2][i] + f[1][j]) % P;for (int k = -m; k <= -1; k++){int res = i - k;if (res <= m && res >= 0)f[2][i] = (f[2][i] + 1) % P;}}for (int i = 3; i <= n; i++){for (int j = 0; j <= m; j++){for (int k = 0; k <= m; k++)f[i][j] = (f[i][j] + f[i - 1][k]) % P;for (int k = 0; k <= m; k++)for (int l = -k; l <= -1; l++) // 枚举负数{int res = j - l;if (res <= m)f[i][j] = (f[i][j] + f[i - 2][k]) % P;}}}int ans = 0;// 最后一个数字可以填负数,需要单独考虑for (int i = 0; i <= m; i++)ans = (ans + f[n][i] + (long long)f[n - 1][i] * i) % P;printf("%d", ans);return 0;
}

不难发现只需要维护 f i , j f_{i,j} fi,j 的前缀和和 j f i , j jf_{i,j} jfi,j 的前缀和即可快速计算。

#include <bits/stdc++.h>
using namespace std;
const int N = 5000, P = 998244353;
int f[N + 5][N + 5], g[N + 5][N + 5];
// f表示直接的前缀和,g表示i*f的前缀和
int main()
{int n, m;scanf("%d%d", &n, &m);if (n == 1){printf("%d", 2 * m + 1);return 0;}f[0][0] = 1;for (int i = 0; i <= m; i++){f[1][i] = i + 1;if (i)g[1][i] = (g[1][i - 1] + i) % P;}for (int i = 0; i <= m; i++){f[2][i] = 2 * m - i + 1;if (i){g[2][i] = (g[2][i - 1] + (long long)f[2][i] * i) % P;f[2][i] = (f[2][i - 1] + f[2][i]) % P;}}for (int i = 3; i <= n; i++){for (int j = 0; j <= m; j++)f[i][j] = (f[i - 1][m] + g[i - 2][m - j] + (long long)(m - j) * (f[i - 2][m] - f[i - 2][m - j] + P) % P) % P;for (int j = 1; j <= m; j++){g[i][j] = (g[i][j - 1] + (long long)f[i][j] * j) % P;f[i][j] = (f[i][j - 1] + f[i][j]) % P;}}// 最后一位特判long long ans = (f[n][m] + g[n - 1][m]) % P;printf("%lld", ans);return 0;
}

整体复杂度 O ( n 2 ) \mathcal O(n^2) O(n2)

L We are the Lights

题意: n × m n\times m n×m 的灯阵,初始全灭。一次操作可以执行:第 i i i 行或列全灭或全亮。问执行完全部 q q q 条操作亮着的灯有多少。 1 ≤ n , m , q ≤ 1 0 6 1 \le n,m,q \le 10^6 1n,m,q106

解法:首先为了防止后面的操作对前面有影响,显然是倒序执行所有的操作。对于一次行操作,只需要维护列中在后续操作中确定会灭或亮的灯数(确定亮的灯在之前行操作中已经计数过了),列同理。因而使用四个变量维护行、列中确定亮、灭的个数即可。整体复杂度 O ( q ) \mathcal O(q) O(q)

#include <bits/stdc++.h>
using namespace std;
const int N = 1000000;
int st[2][N + 5], cnt[2][2];
struct node
{int dir;int id;int op;node(int _dir, int _id, int _op) : dir(_dir), id(_id), op(_op) {}
};
char s[50], t[50];
int main()
{memset(st, -1, sizeof(st));int n[2], q, x;long long ans = 0;scanf("%d%d%d", &n[0], &n[1], &q);vector<node> que;while (q--){scanf("%s%d%s", s, &x, t);int dir = (s[0] == 'c'), op = (t[1] == 'n');que.emplace_back(dir, x, op);}reverse(que.begin(), que.end());for (auto [dir, x, op] : que){if (st[dir][x] != -1)continue;if (op)ans += n[dir ^ 1] - cnt[dir ^ 1][0] - cnt[dir ^ 1][1];st[dir][x] = op;cnt[dir][op]++;   }printf("%lld", ans);return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/12945.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【数据结构】实验十二:图 查找

实验十二 图查找 一、实验目的与要求 1&#xff09;掌握拓扑排序的应用&#xff1b; 2&#xff09;掌握查找的概念和算法&#xff1b; 3&#xff09;掌握查找的基本原理以及各种算法的实现&#xff1b; 4&#xff09;掌握查找的应用。 二、实验内容 1. 用邻接表建立一…

WIZnet W51000S-EVB-PICO 入门教程(一)

概述 W5100S-EVB-Pico是基于树莓派RP2040和全硬件TCP/IP协议栈控制器W5100S的微控制器开发板-基本上与树莓派Pico板相同&#xff0c;但通过W5100S芯片增加了以太网功能。 W5100S-EVB-Pico特点 RP2040规格参数 双核Arm Cortex-M0 133MHz264KB 高速SRAM和2MB板载内存通过…

JAVA基础-多线程入门(详解)

目录 引言 一&#xff0c;线程概念 二&#xff0c;创建线程 2.1&#xff0c;继承Thread类&#xff0c;重写run方法 2.2&#xff0c;实现Runnable接口&#xff0c;重写run方法&#xff0c;实现Runnable接口的实现类的实例对象作为Thread构造函 数的target 2.3&#xff0c;通…

RCU 使用及机制源码的一些分析

》内核新视界文章汇总《 文章目录 1 介绍2 使用方法2.1 经典 RCU2.2 不可抢占RCU2.3 加速版不可抢占RCU2.4 链表操作的RCU版本2.5 slab 缓存支持RCU 3 源码与实现机制的简单分析3.1 数据结构3.2 不可抢占RCU3.3 加速版不可抢占RCU3.4 可抢占RCU3.5 报告禁止状态3.6 宽限期的开…

教雅川学缠论03-分型

原著作者将K线走势分成四中类型&#xff0c;这四中类型&#xff0c;就叫做分型&#xff0c;注意&#xff0c;分型是K线的组合&#xff08;至少3个K线&#xff09;&#xff0c;如下 下面我们以2023年7月武汉控股日K示例 四个分型用图来表示的话&#xff0c;还是很简单的&…

spring5源码篇(13)——spring mvc无xml整合tomcat与父子容器的启动

spring-framework 版本&#xff1a;v5.3.19 文章目录 整合步骤实现原理ServletContainerInitializer与WebApplicationInitializer父容器的启动子容器的启动 相关面试题 整合步骤 试想这么一个场景。只用 spring mvc&#xff08;确切来说是spring-framework&#xff09;&#x…

Flink集群运行模式--Standalone运行模式

Flink集群运行模式--Standalone运行模式 一、实验目的二、实验内容三、实验原理四、实验环境五、实验步骤5.1 部署模式5.1.1 会话模式&#xff08;Session Mode&#xff09;5.1.2 单作业模式&#xff08;Per-Job Mode&#xff09;5.1.3 应用模式&#xff08;Application Mode&a…

从头开始:数据结构和算法入门(时间复杂度、空间复杂度)

目录 文章目录 前言 1.算法效率 1.1 如何衡量一个算法的好坏 1.2 算法的复杂度 2.时间复杂度 2.1 时间复杂度的概念 2.2 大O的渐进表示法 2.3常见时间复杂度计算 3.空间复杂度 4.常见复杂度对比 总结 前言 C语言的学习篇已经结束&#xff0c;今天开启新的篇章——数据结构和算…

gitee 配置ssh 公钥(私钥)

步骤1&#xff1a;添加/生成SSH公钥&#xff0c;码云提供了基于SSH协议的Git服务&#xff0c;在使用SSH协议访问项目仓库之前&#xff0c;需要先配置好账户/项目的SSH公钥。 绑定账户邮箱&#xff1a; git config --global user.name "Your Name" git config --glob…

【图像去噪】基于进化算法——自组织迁移算法(SOMA)的图像去噪研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

HTML基础知识点总结

目录 1.HTML简介 2.HTML基础结构 主要字符&#xff1a; 3.基础知识 &#xff08;一&#xff09;p标签 &#xff08;二&#xff09;hr标签 &#xff08;三&#xff09;尖角号 &#xff08;四&#xff09;版权号 (五)div和span div span (六)列表 &#xff08;1&…

【Docker】Docker相关基础命令

目录 一、Docker服务相关命令 1、启动docker服务 2、停止docker服务 3、重启docker服务 4、查看docker服务状态 5、开机自启动docker服务 二、Images镜像相关命令 1、查看镜像 2、拉取镜像 3、搜索镜像 4、删除镜像 三、Container容器相关命令 1、创建容器 2、查…

pytorch实现梯度下降算法例子

如题&#xff0c;利用pytorch&#xff0c;通过代码实现机器学习中的梯度下降算法&#xff0c;求解如下方程&#xff1a; f ′ ( x , y ) x 2 20 y 2 {f}(x,y) x^2 20 y^2 f′(x,y)x220y2 的最小值。 Latex语法参考&#xff1a;https://blog.csdn.net/ViatorSun/article/d…

RabbitMQ 集群部署

RabbiMQ 是用 Erlang 开发的,集群非常方便,因为 Erlang 天生就是一门分布式语言,但其本身并不支持负载均衡。 RabbitMQ 的集群节点包括内存节点、磁盘节点。RabbitMQ 支持消息的持久化,也就是数据写在磁盘上,最合适的方案就是既有内存节点,又有磁盘节点。 RabbitMQ 模式大…

【动态规划part12】| 309.买卖股票的最佳时机含冷冻期、714.买卖股票的最佳时机含手续费

目录 &#x1f388;LeetCode309.最佳买卖股票时机含冷冻期 &#x1f388;LeetCode714.买卖股票的最佳时机含手续费 &#x1f388;LeetCode309.最佳买卖股票时机含冷冻期 给定一个整数数组prices&#xff0c;其中第 prices[i] 表示第 i 天的股票价格 。​ 设计一个算法计…

Jenkins从配置到实战(二) - Jenkins的Master-Slave分布式构建

前言 Jenkins的Master-Slave分布式构建&#xff0c;就是通过将构建过程分配到从属Slave节点上&#xff0c;从而减轻Master节点的压力&#xff0c;而且可以同时构建多个&#xff0c;有点类似负载均衡的概念。简单理解就是&#xff0c;将Jenkins服务器上的构建任务分配到其他机器…

【Spring Boot】实战:实现优雅的数据返回

实战&#xff1a;实现优雅的数据返回 本节介绍如何让前后台优雅地进行数据交互&#xff0c;正常的数据如何统一数据格式&#xff0c;以及异常情况如何统一处理并返回统一格式的数据。 1.为什么要统一返回值 在项目开发过程中经常会涉及服务端、客户端接口数据传输或前后台分…

多分类问题-Softmax Classifier分类器

概率分布&#xff1a;属于每一个类别的概率总和为0&#xff0c;且都>0&#xff0c;n组类别需要n-1个参数就能算出结果 数据预处理 loss函数 crossentropyloss()函数 CrossEntropyLoss <> LogSoftmax NLLLoss。也就是说使用CrossEntropyLoss最后一层(线性层)是不需要做…

Fiddler Everywhere(TTP调试抓包工具) for Mac苹果电脑版

Fiddler Everywhere for Mac版是Mac电脑上的一款跨平台的HTTP调试抓包工具&#xff0c;Fiddler Everywhere for Mac能够记录客户端与服务器之间的所有HTTP&#xff08;S&#xff09;通信&#xff0c;支持对包进行监视、分析、设置断点、甚至修改请求/响应数据等操作。 适用于任…

基于量子同态加密的安全多方凸包协议

摘要安全多方计算几何(SMCG)是安全多方计算的一个分支。该协议是为SMCG中安全的多方凸包计算而设计的。首先&#xff0c;提出了一种基于量子同态加密的安全双方值比较协议。由于量子同态加密的性质&#xff0c;该协议可以很好地保护量子电路执行过程中数据的安全性和各方之间的…