2025.8.13 测试
prefix
因为 \(SA\) 太一眼了所以没写 \(Hash\) 。
放在排好序的 \(sa\) 上看看我们的问题是什么。发现是有一些关键点,然后要最大化与每个关键点组成的区间最小值之和。
扫描线配单调栈+线段树直接维护,最后取单点最大值。
点击查看
#include <bits/stdc++.h>
#define lep(i, a, b) for (int i = a; i <= b; ++i)
#define rep(i, a, b) for (int i = a; i >= b; --i)
const int _ = 2e6 + 7;
typedef long long ll;
int n, m, len[_], ans[_], Ans, stk[_], top, s[_ << 1]; char t[_]; bool flag[_];
int tp[_], rk[_], ht[_], sa[_], tot[_], V;
int cnt[_ << 2], sum[_ << 2], tag[_ << 2];
void qst() {
lep(i, 1, V) tot[i] = 0; lep(i, 1, n) ++tot[rk[i]];
lep(i, 1, V) tot[i] += tot[i - 1];
rep(i, n, 1) sa[tot[rk[tp[i]]]--] = tp[i];
}
void SA() {
qst();
for (int w = 1, p = 0; w <= n; w <<= 1, V = p, p = 0) {
lep(i, n - w + 1, n) tp[++p] = i;
lep(i, 1, n) if (sa[i] > w) tp[++p] = sa[i] - w;
qst(), std::swap(rk, tp); rk[sa[1]] = p = 1;
lep(i, 2, n) rk[sa[i]] = (tp[sa[i]] == tp[sa[i - 1]] and tp[sa[i] + w] == tp[sa[i - 1] + w]) ? p : ++p;
if (p == n) break;
} int k = 0;
lep(i, 1, n) {
if (rk[i] == 1) continue;
if (k) --k;
while (s[i + k] == s[sa[rk[i] - 1] + k]) ++k;
ht[rk[i]] = k;
}
}
#define ls p << 1
#define rs p << 1 | 1
#define md ((s + t) >> 1)
void pu(int p) { cnt[p] = cnt[ls] + cnt[rs], sum[p] = sum[ls] + sum[rs]; }
void upd(int p, int k) { sum[p] = cnt[p] * k, tag[p] = k; }
void pd(int p) { if (~tag[p]) upd(ls, tag[p]), upd(rs, tag[p]), tag[p] = -1; }
void bd(int s, int t, int p) {
cnt[p] = sum[p] = 0, tag[p] = -1;
if (s == t) return cnt[p] = flag[s], void();
bd(s, md, ls), bd(md + 1, t, rs); pu(p);
}
void mdy(int l, int r, int s, int t, int k, int p) {
if (r < s or t < l) return;
if (l <= s and t <= r) return upd(p, k); pd(p);
mdy(l, r, s, md, k, ls), mdy(l, r, md + 1, t, k, rs); pu(p);
}
int qry(int l, int r, int s, int t, int p) {
if (r < s or t < l) return 0;
if (l <= s and t <= r) return sum[p]; pd(p);
return qry(l, r, s, md, ls) + qry(l, r, md + 1, t, rs);
}
#undef ls
#undef rs
#undef md
void Solve() {
lep(i, 2, m) flag[rk[len[i - 1] + 1] + 1] = true;
bd(1, n, 1), stk[top = 1] = 1; ht[1] = -1;
lep(i, 2, n) {
while (top and ht[i] < ht[stk[top]]) --top;
mdy(stk[top] + 1, i, 1, n, ht[i], 1), stk[++top] = i;
ans[i] = qry(1, i, 1, n, 1);
}
lep(i, 1, n) flag[i] = false;
lep(i, 2, m) flag[rk[len[i - 1] + 1]] = true;
bd(1, n, 1), stk[top = 1] = n + 1; ht[n + 1] = -1;
rep(i, n - 1, 1) {
while (top and ht[i + 1] < ht[stk[top]]) --top;
mdy(i + 1, stk[top] - 1, 1, n, ht[i + 1], 1), stk[++top] = i + 1;
ans[i] += qry(i + 1, n, 1, n, 1);
}
lep(i, 1, len[1] - 1) Ans = std::max(Ans, ans[rk[i]]);
}
int main() {
std::ios::sync_with_stdio(false);
std::cin >> n >> m; n = 0, ++m;
lep(i, 1, m) {
std::cin >> (t + 1), len[i] = std::strlen(t + 1);
lep(j, 1, len[i]) s[++n] = t[j]; s[++n] = 'z' + i;
len[i] += len[i - 1] + 1;
} V = 'z' + m;
lep(i, 1, n) rk[i] = s[i], tp[i] = i;
SA();
Solve(); std::cout << Ans << '\n';
return 0;
}
packback
正常背包,然后将不能选的物品回退掉,\(\ge s\) 的容斥成 \(< s\) 的。
为什么能回退呢?首先根据含义,完全可以将回退物品当做最后一个插入背包的。
那么,背包的过程相当于一个前缀转移,那么只需要逆着退回去就可以还原得到为插入该物品的背包。
点击查看
#include <bits/stdc++.h>
#define lep(i, a, b) for (int i = a; i <= b; ++i)
#define rep(i, a, b) for (int i = a; i >= b; --i)
const int _ = 4000 + 7;
const int mod = 998244353;
typedef long long ll;
int s, n, m, a[_], b[_], c[_], ans, f[_], g[_], _2[_];
std::vector <int> id[_];
int add(int u, int v) { return u + v >= mod ? u + v - mod : u + v; }
int main() {
std::ios::sync_with_stdio(false);
std::cin >> s >> n >> m; _2[0] = 1;
lep(i, 1, n) {
std::cin >> a[i] >> c[i]; id[i].resize(c[i]);
lep(j, 0, c[i] - 1) std::cin >> id[i][j];
}
g[0] = 1;
lep(i, 1, m) {
std::cin >> b[i]; _2[i] = add(_2[i - 1], _2[i - 1]);
rep(j, s, b[i]) g[j] = add(g[j], g[j - b[i]]);
}
lep(i, 1, n) {
lep(j, 0, s) f[j] = g[j];
for (int v : id[i]) lep(j, b[v], s) f[j] = add(f[j], mod - f[j - b[v]]);
int tmp = 0;
lep(j, 0, s - a[i] - 1) tmp = add(tmp, f[j]);
ans = add(ans, add(_2[m - c[i]], mod - tmp));
}
std::cout << ans << '\n';
return 0;
}
djwcb
难点在解密。
然后根据 “交换任意两个元素,逆序对奇偶性改变” 以及 “一个长度为 \(n\) 的轮换可以拆成 \(n-1\) 个对换” 直接做就行了。
点击查看
/*
Start time 18:32
Start code 18:32
End Debug 18:49
Enjoy coding
*/
#include <bits/stdc++.h>
#define lep(i, a, b) for (int i = a; i <= b; ++i)
#define rep(i, a, b) for (int i = a; i >= b; --i)
const int _ = 2e5 + 7;
typedef long long ll;
typedef double db;
typedef std::pair<int, int> PII;
int T, n, A, B, C;
int f[4], U = (1 << 30) - 1;
int gen() {
int g = f[0] ^ (((1ll << 16) * f[0]) & U), h = g ^ (g >> 5);
f[3] = h ^ ((2 * h) & U) ^ f[1] ^ f[2];
f[0] = f[1], f[1] = f[2], f[2] = f[3];
return f[3];
}
int main() {
std::ios::sync_with_stdio(false);
std::cin >> T;
while (T--) { int ans = 0;
std::cin >> n >> A >> B >> C;
f[0] = A & U, f[1] = B & U, f[2] = C & U;
lep(i, 0, n - 1) if (gen() % (n - i)) ans ^= 1;
std::cout << ans;
lep(i, 1, n - 1) {
int a = gen() % n, b = gen() % n, c = gen();
int l = std::min(a, b), r = std::max(a, b), d = (c % n) + 1;
ans ^= (r - l) & 1 & d;
std::cout << ans;
}
std::cout << '\n';
}
return 0;
}
merge
首先,归并操作等价于对前缀 \(max\) 的操作。
如图,每个前缀 \(max\) 对应着 \([l, r]\) 这个原序列的区间。
每次操作相当于将某一个区间裂开,然后再与前面的按照 \(max\) 排序,后面的区间是不用动的。
裂开某个区间后,后面的部分我们直接暴力跳下一个比它大的元素(预处理),然后把新的区间插入。
我们定义势函数 \(\Phi\) 为 有多少个元素未作为前缀 \(max\) ,发现每次暴力跳都会让 \(\Phi\) \(-1\) ,而 \(\Phi\) 最多是 \(O(n)\) 的,所以本做法复杂度是正确的。
点击查看
/*
Start time 18:51
Start code 19:05
End Debug 19:52
AC 20:29
Enjoy coding
*/
#include <bits/stdc++.h>
#define lep(i, a, b) for (int i = a; i <= b; ++i)
#define rep(i, a, b) for (int i = a; i >= b; --i)
const int _ = 2e5 + 7;
typedef long long ll;
typedef double db;
typedef std::pair<int, int> PII;
int n, q, stk[_], tp, pr[_], a[_];
int l[_], r[_], ls[_], rs[_], rnd[_], sz[_], idx, rt, x, y, z;
#define cnt(p) (r[p] - l[p] + 1)
#define val(p) a[l[p]]
void pu(int p) { sz[p] = sz[ls[p]] + sz[rs[p]] + cnt(p); }
int rb(int _l, int _r) { int p = ++idx; rnd[p] = rand(), l[p] = _l, r[p] = _r, sz[p] = cnt(p); return p; }
void slt(int p, int v, int& x, int& y) {
if (!p) return x = y = 0, void();
if (val(p) <= v) x = p, slt(rs[p], v, rs[x], y);
else y = p, slt(ls[p], v, x, ls[y]); pu(p);
}
int mrg(int x, int y) {
if (!x or !y) return x | y;
if (rnd[x] > rnd[y]) { rs[x] = mrg(rs[x], y), pu(x); return x; }
ls[y] = mrg(x, ls[y]), pu(y); return y;
}
void ins(int l, int r) { slt(rt, a[l], x, y), rt = mrg(mrg(x, rb(l, r)), y); }
void del(int v) { slt(rt, v - 1, x, z), slt(z, v, y, z), rt = mrg(x, z); }
void split(int l, int r) { while (l <= r) ins(l, std::min(r, pr[l] - 1)), l = pr[l]; }
void print(int p) {
if (ls[p]) print(ls[p]);
lep(i, l[p], r[p]) std::cout << a[i] << ' ';
if (rs[p]) print(rs[p]);
}
void solve(int k) { int p = rt;
while (true) {
if (k <= sz[ls[p]]) p = ls[p];
else { k -= sz[ls[p]];
if (k <= cnt(p)) break;
k -= cnt(p), p = rs[p];
}
}
std::cout << a[l[p] + k - 1] << ' ';
del(val(p));
split(l[p], l[p] + k - 1), split(l[p] + k, r[p]);
}
int main() {
std::ios::sync_with_stdio(false);
std::cin >> n >> q;
lep(i, 1, n) std::cin >> a[i]; a[n + 1] = n + 1;
rep(i, n + 1, 1) {
while (tp and a[i] > a[stk[tp]]) --tp;
pr[i] = stk[tp], stk[++tp] = i;
}
split(1, n); int p;
lep(i, 1, q) std::cin >> p, solve(p);
std::cout << '\n'; print(rt); std::cout << '\n';
return 0;
}
时间仓促,如有错误欢迎指出,欢迎在评论区讨论,如对您有帮助还请点个推荐、关注支持一下

SA/扫描线/单调栈+背包+弱智题+平衡树
浙公网安备 33010602011771号