2023 年牛客多校第五场题解

A Jujubesister

题意:给定长度为 n n n 的数列 { a } i = 1 n \{a\}_{i=1}^n {a}i=1n q q q 次询问区间 [ l , r ] [l,r] [l,r] 上满足 a i = a k > a j a_i=a_k>a_j ai=ak>aj 且满足 l ≤ i < j < k ≤ r l \le i <j<k \le r li<j<kr 的三元组 ( i , j , k ) (i,j,k) (i,j,k) 数目。 1 ≤ n , q ≤ 5 × 1 0 5 1 \le n,q \le 5\times 10^5 1n,q5×105 1 ≤ a i ≤ n 1 \le a_i \le n 1ain

朴素想法: [ l , r ] → [ l , r + 1 ] [l,r] \to [l, r+1] [l,r][l,r+1] k = r k=r k=r 增加,找在区间范围内的 a i = a r + 1 a_i=a_{r+1} ai=ar+1,离线去查 r + 1 r+1 r+1 前小于 a i a_i ai j j j 个数,

解法:首先考虑如何快速维护这样的三元组。由于有 i < j < k i<j<k i<j<k 的约束不好做,考虑维护每个下标 i i i 前面有多少下标 j j j 满足 1 ≤ j < i 1 \le j<i 1j<i a j < a i a_j<a_i aj<ai,用 c i c_i ci 数组维护。对于一组满足 a i = a k a_i=a_k ai=ak ( i , k ) (i,k) (i,k),则合法的 j j j c j − c i c_j-c_i cjci 个。

( i , j , k ) (i,j,k) (i,j,k) 满足 i < k i<k i<k j < k j<k j<k 满足 a i = a k a_i=a_k ai=ak a j < a k a_j<a_k aj<ak j j j 个数。要求 i < j i<j i<j 的个数,等价于(全集-不合法的) c k − c i c_k-c_i ckci

考虑莫队维护答案,可以再进行一次前缀和,将以 k k k 为三元组右端点的 ( i , j ) (i,j) (i,j) 个数再求和(即对 c k c_k ck 和同色点的 c i c_i ci 做前缀和—— ∑ i = 1 , a i = a k k c k − c i \displaystyle \sum_{i=1, a_i=a_k}^k c_k-c_i i=1,ai=akkckci,动态维护区间前缀中同色点数目 c n t a i cnt_{a_i} cntai 和同色点的和 s u m a i sum_{a_i} sumai)。这样移动区间的时候,可以利用差分快速求出当前的更新量,可以更快维护三元组数目。

因而整体复杂度为莫队的复杂度 O ( n n + n log ⁡ n ) \mathcal O\left(n \sqrt n+n\log n\right) O(nn +nlogn)

#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 = 5e5 + 5;
int n, m, B, S, T, a[N], b[N], c[N], cnt[N];
ll ANS, sum[N], ans[N];
struct Q { int l, r, id; } q[N];
void add(int i) { for (; i <= n; i += i & -i) ++c[i]; }
int qry(int i, int w = 0) { for (; i; i -= i & -i) w += c[i]; return w; }
void addL(int x) {ANS += sum[a[x]] - (ll)cnt[a[x]] * b[x];++cnt[a[x]], sum[a[x]] += b[x];
}
void addR(int x) {ANS += (ll)cnt[a[x]] * b[x] - sum[a[x]];++cnt[a[x]], sum[a[x]] += b[x];
}
void delL(int x) {--cnt[a[x]], sum[a[x]] -= b[x];ANS -= sum[a[x]] - (ll)cnt[a[x]] * b[x];
}
void delR(int x) {--cnt[a[x]], sum[a[x]] -= b[x];ANS -= (ll)cnt[a[x]] * b[x] - sum[a[x]];
}
void Solve() {scanf("%d%d", &n, &m);B = sqrt(n) + 1, S = n / sqrt(m) + 1, T = n / B + 1;fp(i, 1, n) scanf("%d", a + i), b[i] = qry(a[i] - 1), add(a[i]);fp(i, 0, m - 1) scanf("%d%d", &q[i].l, &q[i].r), q[i].id = i;sort(q, q + m, [&](Q a, Q b) { return a.l / S == b.l / S ? (a.l / S & 1 ? a.r > b.r : a.r < b.r) : a.l < b.l; });int L = 1, R = 0;fp(i, 0, m - 1) {int ql = q[i].l, qr = q[i].r;while (L > ql) addL(--L);while (R < qr) addR(++R);while (L < ql) delL(L++);while (R > qr) delR(R--);ans[q[i].id] = ANS; }fp(i, 0, m - 1) printf("%lld\n", ans[i]);
}
int main() {int t = 1;// scanf("%d", &t);while (t--) Solve();return 0;
}

B Circle of Mistery

题意:考虑长度为 n n n 的每个排列 P = { p 1 , p 2 , ⋯ , p n } P=\{p_1,p_2,\cdots,p_n\} P={p1,p2,,pn},并给定权值数组 { w } i = 1 n \{w\}_{i=1}^n {w}i=1n 和权值阈值 k k k,若 P P P 中存在一个任意长度的置换环 { a 1 , a 2 , ⋯ , a l } \{a_1,a_2,\cdots,a_l\} {a1,a2,,al} 满足 ∑ i = 1 l w a i ≥ k \displaystyle \sum_{i=1}^l w_{a_i} \ge k i=1lwaik,则考虑统计该排列 P P P 的逆序对数。问符合条件的排列中最小逆序对数目是多少。 1 ≤ n ≤ 1 0 3 1 \le n \le 10^3 1n103 − 1 0 6 ≤ w i , k ≤ 1 0 6 -10^6 \le w_i,k \le 10^6 106wi,k106

解法:既然需要逆序对数目最少,那么除了给定置换环,那么其他位置 p i p_i pi 一定都是 p i = i p_i=i pi=i 以减少其他部分产生的逆序对数目。

考虑现在置换环构成的数字,假设由 a 1 , a 2 , ⋯ , a l a_1,a_2,\cdots,a_l a1,a2,,al a 1 < a 2 < ⋯ < a l a_1 <a_2 < \cdots <a_l a1<a2<<al)构成,则为了逆序对最少,可以考虑按下面构造: p a 1 = a 2 , p a 2 = a 3 , ⋯ , p a l − 1 = a l , p a l = a 1 p_{a_1}=a_2,p_{a_2}=a_3,\cdots,p_{a_{l-1}}=a_l,p_{a_l}=a_1 pa1=a2,pa2=a3,,pal1=al,pal=a1。此时逆序对数目由 p a l p_{a_l} pal 与前面 p a i p_{a_i} pai 构成 l − 1 l-1 l1 个,然后还有满足 p i = i p_i=i pi=i i ∈ [ a 1 + 1 , a l − 1 ] i \in [a_1+1,a_l-1] i[a1+1,al1] 的数字构成。因而总逆序对数目等于 2 ( a l − a 1 ) − l + 1 2(a_l-a_1)-l+1 2(ala1)l+1。因而可以得到一个转化题意:从 { 1 , 2 , 3 , ⋯ , n } \{1,2,3,\cdots,n\} {1,2,3,,n} 中选出 l l l 个数字 { a 1 , a 2 , ⋯ , a l } \{a_1,a_2,\cdots,a_l\} {a1,a2,,al},满足 ∑ i = 1 l w a i ≥ k \displaystyle \sum_{i=1}^l w_{a_i} \ge k i=1lwaik,求 2 ( a l − a 1 ) − l + 1 2(a_l-a_1)-l+1 2(ala1)l+1 的最小值。

注意到 n ≤ 1 0 3 n \le 10^3 n103,一个朴素的想法是枚举 a 1 a_1 a1,顺推枚举 a l a_l al

纯暴力: [ a 1 , a l ] [a_1,a_l] [a1,al] 区间根据 w i w_i wi 排序,尽可能选大的直到和大于等于 k k k

使用优先队列维护 [ a 1 , a l ] [a_1,a_l] [a1,al] 中哪些数字选出来可以使得其权值和大于等于 k k k,如果和确定大于等于 k k k 则按 2 ( a l − a 1 ) − l + 1 2(a_l-a_1)-l+1 2(ala1)l+1 更新答案。一个贪心的想法是区间中全部的正数必选,尽量选较大的负数,恰好使得其和大于等于 k k k。但是如果朴素实现,就会发现在顺推过程中维护一个优先队列维护负数会出现上限 k − w a l − w a 1 k-w_{a_l}-w_{a_1} kwalwa1 不单调,因而导致优先队列无法快速维护满足条件的负数集合。

这时需要考虑一个性质:边界点 a 1 a_1 a1 a l a_l al w w w 值必然严格大于 0 0 0。考虑固定 a 1 a_1 a1,对于 a l a_l al a l + 1 a_l+1 al+1,假定 w a l > 0 w_{a_l}>0 wal>0 w a l + 1 ≤ 0 w_{a_l+1} \le 0 wal+10。如果选定置换环末端点为 a l + 1 a_l+1 al+1 而非 a l a_l al,则显然选择 a l + 1 a_l+1 al+1 可选数字个数(置换环大小)不会优于 a l a_l al 因为确定多选择一个非正权值 w a l + 1 w_{a_l+1} wal+1,即 l l l 不会变得更大,而 a l + 1 > a l a_l+1>a_l al+1>al,这样 2 ( a l − a 1 ) − l + 1 2(a_l-a_1)-l+1 2(ala1)l+1 一定不会更小。同理对于 a 1 a_1 a1 可以证明类似结论。因而区间两端 a 1 a_1 a1 a l a_l al w w w 值必然严格大于 0 0 0

这时再考虑原始做法。由于确定了 a 1 a_1 a1 a l a_l al 为正数,则当固定 a 1 a_1 a1 而依次增大 a l a_l al 时,随着遍历区间变大,区间中正权值数目和正权值总和会越来越大,这时可以在区间中选择的非正数也会越来越多,这时就可以用一个对顶堆结构维护所有的非正数权值。复杂度 O ( n log ⁡ n ) \mathcal O(n \log n) O(nlogn)

关于对顶堆更加详细一些的说明:考虑使用一个 used小根优先队列表示当前列入区间选择的非正数权值,deleted大根优先队列维护当前区间中未被选择的非正数权值。当加入一个新的非正数权值 x x x 时,先考虑加入 used堆, 如果 deleted堆顶比 x x x 大,则将 x x x 替换入 deleteddeleted堆顶送入 used。然后再维护 used堆使得它与正数权值和加起来大于等于 k k k,可以考虑从 deleted堆顶送入若干个最大的元素,或者 used堆踢出若干最小的。

#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, k, a[N];
void Solve() {scanf("%d%d", &n, &k);int sum = 0, mx = -1e9, ans = 1e9;fp(i, 1, n) {scanf("%d", a + i);mx = max(mx, a[i]);sum += a[i] > 0 ? a[i] : 0;}if (mx >= k) return puts("0"), void();if (sum < k || (sum == 0 && mx < k)) return puts("-1"), void();fp(l, 1, n) {if (a[l] <= 0) continue;priority_queue<int> q1, q2;// q1 : selected non-positive, top = lowest// q2 : unselected non-positive, top = biggestint s = a[l], cnt = 1;for (int r = l + 1; r <= n; ++r) {if (a[r] > 0) s += a[r], ++cnt;else {q2.push(a[r]);while (!q1.empty() && !q2.empty() && -q1.top() < q2.top()) {int x = -q1.top(), y = q2.top();s += x - y, q1.pop(), q2.pop(), q1.push(-y), q2.push(x);}continue;}while (!q2.empty() && s + q2.top() >= k)q1.push(-q2.top()), s += q2.top(), ++cnt, q2.pop();if (s >= k) ans = min(ans, 2 * (r - l) - cnt + 1);}}printf("%d\n", ans);
}
int main() {int t = 1;// scanf("%d", &t);while (t--) Solve();return 0;
}

C Cheeeeen the Cute Cat

题意:给定一个二分图,图左右两部都有 n n n 个点。 ∀ i , j ∈ [ 1 , n ] \forall i,j \in [1,n] i,j[1,n],若 ( i , j + n ) (i,j+n) (i,j+n) 存在边,则 ( j , i + n ) (j,i+n) (j,i+n) 不存在边。使用邻接矩阵给出该图的 n ( n − 1 ) 2 \dfrac{n(n-1)}{2} 2n(n1) 条边。问该图的二分图最大匹配数。 1 ≤ n ≤ 3 × 1 0 3 1 \le n \le 3\times 10^3 1n3×103

解法:考虑将连边 ( i , j + n ) (i,j+n) (i,j+n) 转化到 ( i , j ) (i,j) (i,j) 连边。考虑原二分图上的匹配转化到这个新图上的一条路径—— ( a 1 , a 2 + n ) , ( a 2 , a 3 + n ) , ⋯ , ( a k − 1 , a k + n ) (a_1,a_2+n),(a_2,a_3+n),\cdots,(a_{k-1},a_k+n) (a1,a2+n),(a2,a3+n),,(ak1,ak+n) k − 1 k-1 k1 个点的匹配等价于新图上一条长度为 k k k 的路径 a 1 → a 2 → ⋯ a k a_1 \to a_2 \to \cdots a_k a1a2ak

这时原图转化到一个 n n n 个点的竞赛图(有向完全图)。由于竞赛图必有哈密顿路径,因而答案至少为 n − 1 n-1 n1。考虑何时答案为 n n n,即存在哈密顿回路。显然一个点数大于等于 3 3 3 的强连通分量,必然存在哈密顿回路,也就等价于原图上该子图的完美匹配。因而答案为 n n n 当且仅当图上每个强连通分量都是大于等于 3 3 3。只需要特判掉存在孤立强连通块(大小为 1 1 1)的情况即可。

#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 = 3e3 + 5;
int n, deg[N];
void Solve() {scanf("%d\n", &n);fp(i, 1, n) fp(j, 1, n) {char c = getchar(); getchar();deg[i] += c - '0';}sort(deg + 1, deg + n + 1);for (int i = 1, j = 0, s = 0; i <= n; ++i) {s += deg[i];if (s == i * (i - 1) / 2) {if (i - j < 3) return printf("%d\n", n - 1), void();j = i;}}printf("%d\n", n);
}
int main() {int t = 1;// scanf("%d", &t);while (t--) Solve();return 0;
}

D Cirno’s Perfect Equation Class

题意:给定 c , k , n c,k,n c,k,n,计算满足 k a + b = c ka+b=c ka+b=c 且满足 gcd ⁡ ( a , b ) ≥ n \gcd(a,b) \ge n gcd(a,b)n 的正整数对 ( a , b ) (a,b) (a,b) 个数。 1 ≤ c , k , n ≤ 1 0 9 1 \le c,k,n \le 10^9 1c,k,n109

解法:枚举 c c c 的因子 b b b,计算出 k a ka ka,检查是否可以被 k k k 整除,然后计算出 a a a,最后检查 gcd ⁡ ( a , b ) ≥ n \gcd(a,b) \ge n gcd(a,b)n 即可。复杂度 O ( c log ⁡ c ) \mathcal O\left(\sqrt{c} \log c\right) O(c logc)

#include <bits/stdc++.h>
using namespace std;
int main()
{int q, k, c, n;scanf("%d", &q);while (q--){scanf("%d%d%d", &k, &c, &n);vector<int> factor;for (int i = 1; 1ll * i * i <= c; i++)if (c % i == 0){factor.push_back(i);if (i * i != c)factor.push_back(c / i);}int ans = 0;for (auto b : factor){int left = c - b;if (left % k || left <= 0)continue;int a = left / k;if (__gcd(a, b) >= n)ans++;}printf("%d\n", ans);}return 0;
}

E Red and Blue and Green

题意:构造一个长度为 n n n 的排列,使得满足 m m m 个约束条件: [ l i , r i ] [l_i,r_i] [li,ri] 区间逆序对个数是奇数或偶数。满足这些约束条件的区间包含或不相交。 1 ≤ n , m ≤ 1 0 3 1 \le n, m \le 10^3 1n,m103

解法:首先所有的区间不相交或包含意味着这些区间可以构成一个树形结构。首先考虑使用一个 [ 1 , n ] [1,n] [1,n] 区间包络所有的大区间,并要求区间 [ l , r ] [l,r] [l,r] 上的数字都在 [ l , r ] [l,r] [l,r] 范围。在这个区间包含树(去掉重复区间)上进行 dfs:

  1. 当前节点是叶子节点。如果当前逆序对数目为奇数,则交换区间最开头的两个数字,并返回。如果区间长度为 1 1 1,返回 -1
  2. 当前节点存在子节点 { [ l 1 , r 1 ] , [ l 2 , r 2 ] , ⋯ , [ l k , r k ] } \{[l_1,r_1],[l_2,r_2],\cdots,[l_k,r_k]\} {[l1,r1],[l2,r2],,[lk,rk]}。首先子节点遍历,并统计它们逆序对数目奇偶性,将未被子区间覆盖的区间填上相应数字,即对于当前区间 [ l , r ] [l,r] [l,r],若 x ∈ [ l , r ] x \in [l,r] x[l,r] 不属于任何子区间, a x ← x a_x \leftarrow x axx
  3. 如果当前所有子区间各自内部逆序对奇偶性与大区间相同,直接返回。
  4. 如果当前所有子区间逆序对奇偶性与大区间不同,考虑在第一个区间 [ l 1 , r 1 ] [l_1,r_1] [l1,r1] 和第一个不在第一个区间的位置进行交换。分为以下几种情况:
    1. l 1 = l l_1=l l1=l。考虑交换 r 1 r_1 r1 r 1 + 1 r_1+1 r1+1。如果 r 1 + 1 = l 2 r_1+1=l_2 r1+1=l2,则找到 [ l 2 , r 2 ] [l_2,r_2] [l2,r2] 的最小值与 [ l 1 , r 1 ] [l_1,r_1] [l1,r1] 最大值交换。如果 r 1 + 1 < l 2 r_1+1 <l_2 r1+1<l2,则直接交换 r 1 + 1 r_1+1 r1+1 [ l 1 , r 1 ] [l_1,r_1] [l1,r1] 最大值。
    2. r 1 = r r_1=r r1=r。考虑交换 l 1 − 1 l_1-1 l11 l 1 l_1 l1。找到 [ l 1 , r 1 ] [l_1,r_1] [l1,r1] 的最大值与 l 1 − 1 l_1-1 l11 交换。
#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;
struct Seg { int l, r, k; };
int n, m, a[N], pos[N], deg[N];
vector<Seg> s;
vector<int> G[N];
map<pair<int, int>, int> mp;
void op(int k) { swap(pos[k], pos[k + 1]); }
void dfs(int u) {int w = s[u].k;sort(G[u].begin(), G[u].end(), [&](int a, int b) { return s[a].l < s[b].l; });for (auto v : G[u])dfs(v), w ^= s[v].k;if (!w) return;if (G[u].empty()) op(s[u].l);else if (s[G[u][0]].l == s[u].l) op(s[G[u][0]].r);else op(s[G[u][0]].l - 1);// fp(i, 1, n) printf("%d%c", pos[i], " \n"[i == n]);
}
void Solve() {scanf("%d%d", &n, &m);fp(i, 1, n) pos[i] = i, mp[{i, i}] = 0;for (int l, r, k; m--;) {scanf("%d%d%d", &l, &r, &k);if (mp.count({l, r}) && mp[{l, r}] != k)return puts("-1"), void();if (mp.count({l, r})) continue;mp[{l, r}] = k;s.push_back({l, r, k});}m = s.size();sort(s.begin(), s.end(), [](Seg a, Seg b) { return a.r - a.l < b.r - b.l; });fp(i, 0, m - 1) fp(j, i + 1, m - 1)if (s[j].l <= s[i].l && s[i].r <= s[j].r) {G[j].push_back(i), ++deg[i];break;}fp(i, 0, m - 1)if (!deg[i])dfs(i);fp(i, 1, n) a[pos[i]] = i;fp(i, 1, n) printf("%d%c", a[i], " \n"[i == n]);
}
int main() {int t = 1;// scanf("%d", &t);while (t--) Solve();return 0;
}

G Go to Play Maimai DX

题意:给定长度为 n n n 的数列 { a } i = 1 n = { 1 , 2 , 3 , 4 } n \{a\}_{i=1}^n =\{1,2,3,4\}^n {a}i=1n={1,2,3,4}n,问长度最短的区间包含的 { 1 , 2 , 3 } \{1,2,3\} {1,2,3} 和至少 k k k 4 4 4 1 ≤ k ≤ n ≤ 1 0 5 1 \le k \le n \le 10^5 1kn105

解法:右端点单调右移的时候,左端点必然也单调右移。双指针扫描一下即可。复杂度 O ( n ) \mathcal O(n) O(n)

#include<bits/stdc++.h>
using namespace std;
#define fre(x) freopen(#x".in","r",stdin);freopen(#x".out","w",stdout)
typedef long long ll;
template<typename T>inline void read(T &a){char c=getchar();T x=0,f=1;while(!isdigit(c)){if(c=='-')f=-1;c=getchar();}while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}a=f*x;
}
inline void write(ll x){char P[105];int w=0;if(x<0)putchar('-'),x=-x;if(x==0)printf("0");while(x)P[++w]=x%10+'0',x/=10;for(int i=w;i;i--)putchar(P[i]);
}
const int N=1e5+10;
int cnt[N];
int a[N];
int n,k;
bool pd(){for(int i=1;i<=3;++i){if(!cnt[i])return false;}if(cnt[4]<k)return false;return true;
}
int main()
{scanf("%d%d",&n,&k);for(int i=1;i<=n;++i)scanf("%d",&a[i]);int minlen=n;for(int l=1,r=0;r<=n&&l<=n;++l){while((!pd())&&r<n){cnt[a[++r]]++;}if(pd())minlen=min(minlen,r-l+1);cnt[a[l]]--;}printf("%d\n",minlen);return 0;
}

H Nazrin the Greeeeeedy Mouse

题意:给定 n n n 个奶酪,体积为 a i a_i ai,价值为 b i b_i bi,只能从 1 1 1 n n n 顺序拿,如果要拿第 i + 1 i+1 i+1 个奶酪,第 i i i 个奶酪必须拿走或者打洞,被打洞的奶酪不能再被拿走。给定 m m m 个背包,体积为 { s i z e } i = 1 m \{{\rm size}\}_{i=1}^m {size}i=1m,每次拿着一个背包从第一个奶酪出发拿奶酪,问 m m m 个背包下能拿走多少价值的奶酪。 1 ≤ n ≤ 200 1\le n \le 200 1n200 1 ≤ a i ≤ 200 1 \le a_i\le 200 1ai200 1 ≤ b i ≤ 1 0 5 1 \le b_i\le 10^5 1bi105 1 ≤ s i z e i ≤ 200 1\le {\rm size}_i \le 200 1sizei200 1 ≤ m ≤ 1 0 5 1\le m \le 10^5 1m105

解法:不难注意到,每次有效拿背包装奶酪至少会拿一个奶酪走。因而至多只需要 min ⁡ ( n , m ) \min(n,m) min(n,m) 个背包即可,并且一定是贪心取最大的背包。并且每个奶酪只有拿走或者不拿走(被打洞),而且还是顺序拿走。

考虑背包: f i , j , k f_{i,j,k} fi,j,k 表示从第 i i i 个奶酪开始拿,拿到第 j j j 个奶酪取得体积为 k k k 的最大价值。一个非常经典的 01 背包:
f i , j , k ← max ⁡ ( f i , j − 1 , k , f i , j − 1 , k − a i + b i ) f_{i,j,k} \leftarrow \max(f_{i,j-1,k},f_{i,j-1,k-a_i}+b_i) fi,j,kmax(fi,j1,k,fi,j1,kai+bi)
维护该背包的复杂度为 O ( n 2 V ) \mathcal O(n^2V) O(n2V)。然后考虑维护前缀 min ⁡ \min min f i , j , k ← max ⁡ ( f i , j , k − 1 , f i , j , k ) f_{i,j,k} \leftarrow \max(f_{i,j,k-1},f_{i,j,k}) fi,j,kmax(fi,j,k1,fi,j,k)

然后再维护一个 dp: g i , j g_{i,j} gi,j 表示用了 i i i 个背包,现在已经拿到第 j j j 个奶酪的最大价值。转移是显然的:
g i , j ← max ⁡ k ∈ [ 0 , j − 1 ] g i − 1 , k + f i , j , s i z e i g_{i,j} \leftarrow \max_{k \in [0,j-1]}g_{i-1,k}+f_{i,j,{\rm size}_{i}} gi,jk[0,j1]maxgi1,k+fi,j,sizei
因而总复杂度 O ( n 2 V + n V ) \mathcal O(n^2V+nV) O(n2V+nV)

#include <bits/stdc++.h>
using namespace std;
const int N = 200;
int a[N + 5], siz[100005], use[N + 5];
int f[N + 5][N + 5][N + 5], b[N + 5], g[N + 5][N + 5];
int main()
{int n, m;scanf("%d%d", &n, &m);for (int i = 1; i <= n; i++)scanf("%d%lld", &a[i], &b[i]);for (int i = 1; i <= n; i++){for (int j = i; j <= n; j++){for (int w = 0; w <= N; w++)f[i][j][w] = f[i][j - 1][w];for (int w = a[j]; w <= N; w++)f[i][j][w] = max(f[i][j][w], f[i][j - 1][w - a[j]] + b[j]);}for (int j = 1; j <= n; j++)for (int w = 1; w <= N; w++)f[i][j][w] = max(f[i][j][w], f[i][j][w - 1]);}for (int i = 1; i <= m; i++)scanf("%d", &siz[i]);int cnt = 0, ans = 0;for (int i = max(1, m - n); i <= m; i++)use[++cnt] = siz[i];for (int i = 1; i <= cnt; i++)for (int j = 1; j <= n; j++){for (int k = 0; k < j; k++)g[i][j] = max(g[i][j], g[i - 1][k] + f[k + 1][j][use[i]]);ans = max(ans, g[i][j]);}printf("%d", ans);return 0;
}

I The Yakumo Family

题意:给定长度为 n n n 的数列 { a } i = 1 n \{a\}_{i=1}^n {a}i=1n,定义 f ( l , r ) = ⨁ i = l r a i \displaystyle f(l,r)=\bigoplus_{i=l}^r a_i f(l,r)=i=lrai,求下式:
∑ 1 ≤ l 1 ≤ r 1 ≤ n ∑ r 1 < l 2 ≤ r 2 ≤ n ∑ r 2 < l 3 ≤ r 3 ≤ n f ( l 1 , r 1 ) × f ( l 2 , r 2 ) × f ( l 3 , r 3 ) \sum_{1 \le l_1 \le r_1 \le n} \sum_{r_1 <l_2 \le r_2 \le n}\sum_{r_2 < l_3\le r_3 \le n} f(l_1,r_1)\times f(l_2,r_2) \times f(l_3,r_3) 1l1r1nr1<l2r2nr2<l3r3nf(l1,r1)×f(l2,r2)×f(l3,r3)
1 ≤ n ≤ 2 × 1 0 5 1 \le n \le 2\times 10^5 1n2×105 0 ≤ a i ≤ 1 0 9 0 \le a_i \le 10^9 0ai109

解法:显然对于本题应该考虑拆位。

首先考虑如何求
∑ 1 ≤ l 1 ≤ r 1 ≤ n f ( l 1 , r 1 ) \sum_{1 \le l_1 \le r_1 \le n} f(l_1,r_1) 1l1r1nf(l1,r1)
以拆位视角,仅考虑第 k k k 个二进制位的贡献。当对 { a } \{a\} {a} 进行前缀异或操作(得到 { s } i = 0 n \{s\}_{i=0}^n {s}i=0n)后,对于所有右端点 r 1 r_1 r1 确定的区间 [ x , r 1 ] [x,r_1] [x,r1],该区间在该二进制位上贡献为 1 1 1 的次数为所有 s x − 1 ≠ s r 1 s_{x-1} \ne s_{r_1} sx1=sr1 且满足 x ≤ r 1 x \le r_1 xr1 的下标 x x x 数目,且每次出现对上式的贡献都为 1 1 1。因而可以考虑随着 r 1 r_1 r1 增大维护数组 P 0 / P 1 P_0/P_1 P0/P1 表示该位上前缀异或和 s x s_x sx 0 / 1 0/1 0/1 的下标 x x x 次数。

因而综合每一个二进制位,我们可以维护 { S 1 } i = 1 n \{S_1\}_{i=1}^n {S1}i=1n 数组,其中 S 1 , i S_{1,i} S1,i 表示区间右端点固定在 i i i 的区间的异或和的和。这时考虑对 S 1 , i S_{1,i} S1,i 进行前缀和操作得到 { T 1 } \{T_{1}\} {T1} 数组,则 T 1 , n T_{1,n} T1,n 即为 ∑ 1 ≤ l 1 ≤ r 1 ≤ n f ( l 1 , r 1 ) \displaystyle \sum_{1 \le l_1 \le r_1 \le n} f(l_1,r_1) 1l1r1nf(l1,r1)

接下来考虑求
∑ 1 ≤ l 1 ≤ r 1 ≤ n ∑ r 1 < l 2 ≤ r 2 ≤ n f ( l 1 , r 1 ) × f ( l 2 , r 2 ) \sum_{1 \le l_1 \le r_1 \le n}\sum_{r_1 < l_2 \le r_2 \le n} f(l_1,r_1)\times f(l_2,r_2) 1l1r1nr1<l2r2nf(l1,r1)×f(l2,r2)
首先还是考虑拆位。考虑当 r 2 r_2 r2 固定为 i i i 时,所有区间 [ x , r 2 ] [x,r_2] [x,r2] 如果该位上异或和为 1 1 1,必须满足 s x − 1 ≠ s r 2 s_{x-1} \ne s_{r_2} sx1=sr2 且满足 x ≤ r 2 x \le r_2 xr2 的下标 x x x,且每个 x x x 对答案的贡献为 T 1 , x − 1 T_{1,x-1} T1,x1——即第一区间的右端点在 [ 1 , x − 1 ] [1,x-1] [1,x1] 上。因而可以考虑随着 r 1 r_1 r1 增大维护数组 P 0 / P 1 P_0/P_1 P0/P1 表示该位上前缀异或和 s x s_x sx 0 / 1 0/1 0/1 T 1 , x T_{1,x} T1,x 的和。

这时综合每个二进制位,就可以维护 { S 2 } i = 1 n \{S_2\}_{i=1}^n {S2}i=1n 数组表示第二区间右端点固定在 i i i 的区间的异或乘积和。

因而对于如果有 k k k 个不相交区间,也可以类似处理。本题 k = 3 k=3 k=3,因而只需要进行三次。总复杂度 O ( k n log ⁡ a i ) \mathcal O(kn \log a_i) O(knlogai)

#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 = 2e5 + 5, M = 30, P = 998244353;
int n, a[N], f[N], s[N];
void Solve() {scanf("%d", &n);s[0] = 1;fp(i, 1, n) scanf("%d", a + i), a[i] ^= a[i - 1], s[i] = 1;fp(_, 1, 3) {fp(j, 0, M) {ll c[2] = {s[0], 0};fp(i, 1, n) {f[i] = (f[i] + (c[(a[i] >> j & 1) ^ 1] << j)) % P;(c[a[i] >> j & 1] += s[i]) %= P;}}s[0] = 0;fp(i, 1, n) s[i] = (s[i - 1] + f[i]) % P, f[i] = 0;}printf("%d\n", s[n]);
}
int main() {int t = 1;// scanf("%d", &t);while (t--) Solve();return 0;
}

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

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

相关文章

课程作业-基于Python实现的迷宫搜索游戏附源码

简单介绍一下 该项目不过是一个平平无奇的小作业&#xff0c;基于python3.8开发&#xff0c;目前提供两种迷宫生成算法与三种迷宫求解算法&#xff0c;希望对大家的学习有所帮助。 项目如果有后续的跟进将会声明&#xff0c;目前就这样吧~ 效果图如下所示&#xff1a; 环境…

14 springboot项目——首页跳转实现

templates里的静态资源无法访问&#xff0c;需要写mvc的配置类或者改application.xml配置文件实现首页访问。这两个方式用其中一种即可&#xff0c;否则会冲突。 14.1 首页跳转方式一 创建配置类&#xff0c;在config包中创建一个mvc的配置类&#xff1a; package jiang.com.s…

设备管理系统与物联网的融合:实现智能化设备监控和维护

在数字化时代&#xff0c;设备管理系统和物联网技术的融合为工业企业带来了巨大的变革和创新。本文将探讨设备管理系统与物联网的融合&#xff0c;重点介绍设备健康管理平台在实现智能化设备监控和维护方面的关键作用和优势。 一、设备管理系统与物联网的融合 随着物联网技术的…

windows脚本获取管理员权限修改host

很多时候我们常常需要通过管理员权限执行脚本&#xff0c;脚本可能涉及到一些受保护信息的访问&#xff0c;我们写个简单的脚本来更改host文件&#xff0c;host文件就是需要管理员权限才能访问的启动脚本时先检查是否有管理员权限&#xff0c;如果没有就调用授权脚本进行管理员…

BPMNJS插件使用及汉化(Activiti绘制流程图插件)

BPMNJS插件运行最重要的就是需要安装nodejs插件,这不一定要安装和测试好。 主要是使用npm命令 1、配置BPMNJS插件绘制activiti7工作流 1.1、安装和配置nodejs 插件 1.1.1、下载nodejs 下载地址:https://nodejs.org/en 1.1.2、安装nodejs,傻瓜式安装 安装之后在安装…

TypeScript知识点总结

typescript是js的超集&#xff0c;目前很多前端框架都开始使用它来作为项目的维护管理的工具&#xff0c;还在不断地更新&#xff0c;添加新功能中&#xff0c;我们学习它&#xff0c;才能更好的在的项目中运用它&#xff0c;发挥它的最大功效 let b: null nulllet c: null …

Mac提示文件:已损坏,无法打开。你应该把它移到废纸篓

文章目录 一、电脑信息二、打开任何来源设置三、更改应用程序拓展属性 一、电脑信息 我的是新版的Venture 13的系统。UI改的比较多。与之前的配置还是有很大的区别的。 打开下载的软件&#xff0c;显示已经损坏&#xff0c;打不开。抛开软件本身的问题外&#xff0c;一般是Ma…

vue3过滤输入框首尾空格

vue3过滤输入框首尾空格 在 directive文件夹下 新建 trim.ts 文件 // trim.ts 文件 import { App } from "vue"function getInput(el: { tagName: string; querySelector: (arg0: string) > any }) {let inputEleif (el.tagName ! INPUT) {inputEle el.queryS…

微信云开发-数据库操作

文章目录 前提初始化数据库插入数据查询数据获取一条数据获取多条数据查询指令 更新数据更新指令 删除数据总结 前提 首先有1个集合(名称:todos). 其中集合中的数据为: {// 计划描述"description": "learn mini-program cloud service",// 截止日期"…

LeetCode 626. 换座位

题目链接&#xff1a;LeetCode 626. 换座位 题目描述 表名&#xff1a;Seat 编写SQL查询来交换每两个连续的学生的座位号。如果学生的数量是奇数&#xff0c;则最后一个学生的id不交换。 按 id 升序 返回结果表。 查询结果格式如下所示。 示例1&#xff1a; 题目分析 如…

医疗知识图谱问答 ——Neo4j 基本操作

前言 说到问答机器人&#xff0c;就不得不说一下 ChatGPT 啦。一个预训练的大预言模型&#xff0c;只要是人类范畴内的知识&#xff0c;似乎他回答得都井井有条&#xff0c;从写文章到写代码&#xff0c;再到解决零散琐碎的问题&#xff0c;不光震撼到我们普通人&#xff0c;就…

微信消息撤回时间延长到2小时了?

““微信发出2小时后也可撤回,上班族的福音. ” 近日&#xff0c;有传言称 微信撤回消息的时间将延长至2小时 引起舆论关注 微信作为国内最大的网络社交平台&#xff0c;目前用户已超过11亿。 虽然微信已经做的很不错了&#xff0c;但微信消息撤回这一功能&#xff0c;还是…

【第一阶段】kotlin语言的String模板

1.在Java中拼接字符串使用的是“” 2.在kotlin中使用"${}" 3.kotlin语言中if是表达式&#xff0c;更灵活 fun main() {val city"西安"val time24//java中写法println("我在"city"玩了"time"小时")//kotlin中写法&#xff0…

jenkins通过sshPut传输文件的时候,报错Permission denied的SftpException

一、背景 使用jenkins的ssh插件传输文件至远程机器的指定目录&#xff0c;php程序打包后&#xff0c;经过zip压缩为oms.zip zip -rq oms.zip ./ -x .git/* -x .env然后我们求md5值 md5sum oms.zip最后执行传输。 09:03:02 Executing command on ssh[116.61.10.149]: mkdir…

使用docker安装wordpress详细教程及出现数据库无法连接问题解决方法

1.获取wordpress镜像 docker pull wordpress 2.创建wordpress 的容器 a.创建wordpress的文件镜像卷文件夹 mkdir wordpress b.创建wordpress镜像 docker run --name wp -p8080:80 -v /home/wordpress/:/var/www/html -d wordpress c.查看容器运行情况 3.在本地或者其他服务器创…

Astro + Vercel 快速搭建自己的博客网站

Astro 和 Vercel 彼此相得益彰&#xff0c;前者提供出色的开发者体验&#xff0c;用于构建现代静态站点&#xff0c;而后者负责部署和托管代码。 两者结合我们就可以轻轻松松零成本搭建自己的博客网站。查看示例。 步骤 1&#xff0c;创建评论仓库 在部署博客之前&#xff…

深入学习了解 gRPC协议

gRPC 是一个高性能、通用的开源RPC框架&#xff0c;其由 Google 主要面向移动应用开发并基于HTTP/2 协议标准而设计&#xff0c;基于 ProtoBuf(Protocol Buffers) 序列化协议开发&#xff0c;且支持众多开发语言。本文作者深入研究了 gRPC 协议&#xff0c;对协议本身作出解构。…

Linux 命令速查表

Linux 命令速查表 命令速查表系统硬件用户登陆文件进程安装包文件权限安装源(编译)搜索网络文件传输磁盘使用情况目录遍历学习网址命令速查表 系统 uname显示linux系统信息 uname -r显示内核版本信息 uptime显示系统运行的时间 (包括平均负载) hostname显示系统主机名 ho…

[用go实现解释器]笔记1-词法分析

本文是《用go实现解释器》的读书笔记 ​ https://malred-blog​malred.github.io/2023/06/03/ji-suan-ji-li-lun-ji-shu-ji/shi-ti/go-compile/yong-go-yu-yan-shi-xian-jie-shi-qi/go-compiler-1/#toc-heading-6http://个人博客该笔记地址 ​github.com/malred/malanghttp:/…

SAS-数据集SQL垂直(纵向)合并

一、SQL垂直合并的基本语法 一个selectt对应一个表&#xff0c;select之间用set-operator连接&#xff0c;set-operator包括&#xff1a;except&#xff08;期望&#xff09;、intersect&#xff08;相交&#xff09;、union&#xff08;合并&#xff09;&#xff0c;outer un…