2025CSP-S模拟赛22 比赛总结

2025CSP-S模拟赛22

T1 T2 T3 T4
60 TLE 0 TLE 0 TLE 0 TLE

排名:11/21;总分:60

T1 写了线段树并不断卡常,妄想 \(O(n \log n)\)\(10^7\)。后三题就是没写出来。

T1 草莓列车(train)

这边我要展示我考试时不断卡常后的区间取 max、单点查询的线段树,非常帅气。

unsigned int mx[4 * N];
#define lc p << 1
#define rc p << 1 | 1
#define mid (l + r >> 1)
inline void build(int p, int l, int r) {
	if (l == r) return mx[p] = a[l], void();
	build(lc, l, mid), build(rc, mid + 1, r);
}
inline void update(int p, int l, int r, int x, int y, unsigned int v) {
	return (x <= l && r <= y ? mx[p] = max(mx[p], v): ((x <= mid ? update(lc, l, mid, x, y, v), 0 : 0),(mid < y ? update(rc, mid + 1, r, x, y, v), 0 : 0))), void();
}
inline unsigned int query(int p, int l, int r, int x, unsigned int tag) {
	return l == r ? max(mx[p], tag) : (x <= mid ? query(lc, l, mid, x, max(tag, mx[p])) : query(rc, mid + 1, r, x, max(tag, mx[p])));
}

直接考虑正解。

首先,我们关注到有一个东西叫做 ST 表,他可以实现 \(O(n\log n)\) 预处理,\(O(1)\) 查询区间极值。那我们现在考虑把他一整个反过来,让他实现 \(O(1)\) 区间修改,然后 \(O(n\log n)\) 处理出完整的 ST 表实现单点查询。直接上代码。

#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10;
int n, m, typ;
unsigned int a[N];
unsigned int xx, seed;
inline unsigned int getnum() {
	xx = (xx << 3) ^ xx;
	xx = ((xx >> 5) + seed) ^ xx;
	return xx;
}
int Log2[N];
unsigned int f[N][20];
int main() {
	ios::sync_with_stdio(0); cin.tie(0), cout.tie(0);
	cin >> n >> m >> typ;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
	}
	for (int i = 2; i <= n; i++) Log2[i] = Log2[i / 2] + 1;
	cin >> xx >> seed;
	int l, r, s;
	unsigned int v;
	for (int id = 1; id <= m; id++) {
		l = getnum() % n + 1, r = getnum() % n + 1;
		v = getnum();
		l > r ? (l ^= r ^= l ^= r) : 0;
		l = (typ == 1 ? 1 : l);
		//---- l r v
		s = Log2[r - l + 1];
		f[l][s] = max(f[l][s], v);
		f[r - (1 << s) + 1][s] = max(f[r - (1 << s) + 1][s], v);
	}
	for (int j = Log2[n]; j >= 1; j--) {
		for (int i = 1; i + (1 << j) - 1 <= n; i++) {
			f[i][j - 1] = max(f[i][j - 1], f[i][j]);
			f[i + (1 << j - 1)][j - 1] = max(f[i + (1 << j - 1)][j - 1], f[i][j]);
		}
	}
	for (int i = 1; i <= n; i++) {
		cout << max(a[i], f[i][0]) << ' ';
	}
	
	return 0;
}

T2 草莓路径(path)

CSP-S模拟赛 T2 出神秘线性基。。反正我是改不了一点。

T3 草莓城市(city)

CSP-S模拟赛 T3 出神秘 2-SAT。。反正我是改不了一点。

T4 草莓之歌(easy)

CSP-S模拟赛 T4 出神秘黑色 dp。。反正我是改不了一点。

这篇我认为写挺好的

首先,容易发现一个字符串能合法当且仅当第 \(i\) 个 A 在第 \(i\) 个 B 钱。那么一定是第 \([l,r]\) 个 A 去匹配第 \([l,r]\) 个 B。那么设 \(c_i\) 表示在第 \(i\) 个 A 前的 B 的个数,\(w(l,r)\) 表示将第 \([l+1,r]\) 个 A 和 B 尽心匹配的代价。则 \(w(l,r)=\sum_{i=l+1}^r \max(0,c_i-l)\),相当于统计每个 A 交换的 B 的数量。

考虑 dp。设 \(f_{i,l}\) 表示当前划分出来了 \(l\) 个子序列,下一个子序列开头是第 \(i+1\) 个 A 的最小代价。转移即为 \(f_{i,l}+w(i,j) \rightarrow f_{j,l+1}\)。此时就解决了问题,时间复杂度 \(O(n^3)\)

以上的部分都很好懂,后面就开了。我自己是费了很大精力理解的。

首先,\(w(i,j)\) 满足四边形不等式[1],这个画个图就能理解。

然后,得出 \(f_{n,i}\) 是满足凸性的,而且是下凸包。题解给了个证明,反正我没看。UKE_Automation 给我讲的感性理解我也不是很理解。反正把这个结论记一下吧,后面能用了用。(满足四边形不等式的序列划分问题的答案凸性)

然后就使用 wqs 二分进行优化[2],这个东西比较牛逼。反正然后 dp 式子就变为 \(f_j \leftarrow f_i+w(i,j)-mid\)

进而考虑设 \(p_i\) 表示第一个满足 \(c_k \geq i\)\(k\),且 \(p_i=\max(p_i,i+1)\),则 \(w(l,r)=\sum_{i=p_l}^r c_i-l\)。设 \(c_i\) 的前缀和为 \(s_i\),则 \(w(l,r)=s_r-s_{p_l-1}-l\times(r-p_l+1)\)

然后你发现这个 dp 是一个 1d1d 的转移,然后上斜率优化 dp [3]即可。

代码贴详细一点。

40pts:

memset(f, 0x3f, sizeof(f));
f[0][0] = 0;
for (int i = 0; i < n; i++) {
    for (int l = 0; l <= kk; l++) {
        for (int j = i + 1; j <= n; j++) {
            f[j][l + 1] = min(f[j][l + 1], f[i][l] + w(i, j));
        }
    }
}

60pts:

#include <bits/stdc++.h>
#define int long long
#define il inline

using namespace std;

const int INF = 0x3f3f3f3f3f3f3f3f;
const int N = 1e6 + 10;
int n, kk, a[2 * N];
char ss[2 * N];
int c[N], p[N], s[N];
int f[N], g[N]; 
il bool check(int mid) {
	for (int i = 1; i <= n; i++) {
		f[i] = g[i] = INF;
		for (int j = 0; j < i; j++) {
			int v = f[j] - mid;
			if (p[j] <= i) v += s[i] - s[p[j] - 1] - j * (i - p[j] + 1);
			if (v < f[i] || (v == f[i] && g[j] + 1 < g[i])) {
				f[i] = v, g[i] = g[j] + 1;
			}
		}
	}
	return g[n] <= kk;
}
signed main() {
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	cin >> n >> kk >> ss;
	for (int i = 1; i <= 2 * n; i++) a[i] = (ss[i - 1] == 'A');
	int cnt = 0, tot = 0;
	for (int i = 1; i <= 2 * n; i++) {
		if (a[i] == 1) c[++tot] = cnt;
		else cnt++;
	}
	for (int i = 1; i <= n; i++) {
		p[i] = lower_bound(c + 1, c + 1 + n, i) - c;
		p[i] = max(p[i], i + 1);
	}
	for (int i = 1; i <= n; i++) {
		s[i] = s[i - 1] + c[i];
	}
	int L = -INF, R = 0, ans = 0;
	while (L <= R) {
		int mid = (L + R) / 2;
		if (check(mid)) {
			ans = mid;
			L = mid + 1;
		} else {
			R = mid - 1;
		}
	}
	check(ans);
	cout << f[n] + kk * ans << "\n";
	
	return 0;
}

100pts:

#include <bits/stdc++.h>
#define int long long
#define il inline

using namespace std;

const int INF = 0x3f3f3f3f3f3f3f3f;
const int N = 2e6 + 10;
int n, kk, a[N];
char ss[N];
int c[N], p[N], s[N];
int f[N], g[N]; 
int mid;
il int y(int j) {return -f[j] + s[p[j] - 1] - j * p[j] + j;}
il int k(int i) {return i;}
il int x(int j) {return -j;}
il int b(int i) {return -f[i] - mid + s[i];}
il double slope(int i, int j) {
	return 1.0 * (y(i) - y(j)) / (x(i) - x(j));
}
int q[N], head, tail;
vector<int> num[N];
il bool check(int mm) {
	mid = mm;
	for (int i = 1; i <= n; i++) f[i] = g[i] = INF;
	head = 1, tail = 0;
	for (int i = 1; i <= n; i++) {
		for (int j : num[i]) {
			while (head < tail && slope(q[tail - 1], q[tail]) > slope(q[tail], j)) tail--;
			q[++tail] = j;
		}
		while (head < tail && slope(q[head], q[head + 1]) < k(i)) head++;
		f[i] = -mid + s[i] - y(q[head]) + k(i) * x(q[head]);
		g[i] = g[q[head]] + 1;
	}
	return g[n] <= kk;
}
signed main() {
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
	cin >> n >> kk >> ss;
	for (int i = 1; i <= 2 * n; i++) a[i] = (ss[i - 1] == 'A');
	int cnt = 0, tot = 0;
	for (int i = 1; i <= 2 * n; i++) {
		if (a[i] == 1) c[++tot] = cnt;
		else cnt++;
	}
	for (int i = 0; i <= n; i++) {
		p[i] = lower_bound(c + 1, c + 1 + n, i) - c;
		p[i] = max(p[i], i + 1);
		num[p[i]].push_back(i);
	}
	for (int i = 1; i <= n; i++) {
		s[i] = s[i - 1] + c[i];
	}
	int L = -INF, R = 0, ans = 0;
	while (L <= R) {
		mid = (L + R) / 2;
		if (check(mid)) {
			ans = mid;
			L = mid + 1;
		} else {
			R = mid - 1;
		}
	}
	check(ans);
	cout << f[n] + kk * ans << "\n";
	
	return 0;
}

  1. 如果对于二元函数 \(w(i,j)\) 满足对于 \(\forall l_1\le l_2 \le r_1 \le r_2\) 都有 \(w(l_1,r_1)+w(l_2,r_2)\le w(l_1,r_2)+w(l_2,r_1)\),则称 \(w(i,j)\) 满足四边形不等式。 ↩︎

  2. 自己去百度啊。就是二分一个斜率,然后非常牛逼进行一堆操作优化了复杂度并降维。 ↩︎

  3. 推销:斜率优化DP - Zctf1088 - 博客园 (cnblogs.com)↩︎

posted @ 2025-07-20 14:18  Zctf1088  阅读(40)  评论(0)    收藏  举报