Loading

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;
}

posted @ 2025-08-13 20:55  qkhm  阅读(16)  评论(0)    收藏  举报