2025CSP-S模拟赛38 比赛总结
2025CSP-S模拟赛38
| T1 | T2 | T3 | T4 |
|---|---|---|---|
| 30 TLE | 0 WA | 0 WA | 27 TLE |
总分:57;排名:16/22
T1 玩了很久无果,拼了个暴力。二三不会,T4 是暴力。
T1 黑暗料理
赛时觉得此题很 2-SAT。但是由于对于 2-SAT 板子的理解不足以及并没有意识到:2-SAT 只能求出一种合法的解,并无法求出最优解。然后我们就失败了。
正解来了。考虑到只有 1+1 和奇数+偶数会产生质数。然后呢,你发现这个多于一个的 1 都是无用的。你现在只保留一个 1,其他的 1 扔掉,然后把加和为质数的点连边,你就发现这构成了一张二分图(奇数点作为左部,偶数点作为右部),答案就是此图的最大独立集。
题解给的是拿网络流求这个最大独立集(家人们,S 组 T1 考网络流),但是其实匈牙利是可做的。你发现这个图有 \(n\) 个点,就很无脑的去想,最多有 \((\frac{n}{2})^2\) 条边,然后你的复杂度就是一个 \(\frac{n^3}{4}\),然后很妙啊,你的 \(T \le 4\)。然后 750 完全跑得过 \(n^3\)。
#include <bits/stdc++.h>
#define il inline
using namespace std;
const int bufsz = 1 << 20;
char ibuf[bufsz], *p1 = ibuf, *p2 = ibuf;
#define getchar() (p1 == p2 && (p2 = (p1 = ibuf) + fread(ibuf, 1, bufsz, stdin), p1 == p2) ? EOF : *p1++)
il int read() {
int x = 0; char ch = getchar(); bool t = 0;
while (ch < '0' || ch > '9') {t ^= ch == '-'; ch = getchar();}
while (ch >= '0' && ch <= '9') {x = (x << 1) + (x << 3) + (ch ^ 48); ch = getchar();}
return t ? -x : x;
}
const int N = 1500 + 10;
int n, a[N];
il int fpow(int a, int x, int MOD) {
int ans = 1;
while (x) {
if (x & 1) ans = 1ll * ans * a % MOD;
a = 1ll * a * a % MOD;
x >>= 1;
}
return ans;
}
int Test[20] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37};
int test_time = 12;
bool isPrime(int p) {
if (p < 2) return 0;
int t = p - 1, k = 0;
while (!(t & 1)) {
t >>= 1;
k++;
}
for (int i = 0; i < test_time; i++) {
if (p == Test[i]) return 1;
int a = fpow(Test[i], t, p);
for (int j = 1; j <= k; j++) {
if (1ll * a * a % p == 1 && a != 1 && a != p - 1) {
return 0;
}
a = 1ll * a * a % p;
}
if (a != 1) return 0;
}
return 1;
}
vector<int> G[N];
int vis[N], pei[N];
il bool dfs(int x) {
for (int y : G[x]) {
if (vis[y]) continue;
vis[y] = 1;
if (!pei[y] || dfs(pei[y])) {
pei[y] = x;
return true;
}
}
return false;
}
int t[N];
int solve() {
n = read();
for (int i = 1; i <= n; i++) G[i].clear();
for (int i = 1; i <= n; i++) {
a[i] = read();
}
int cnt = 0;
for (int i = 1; i <= n; i++) t[i] = 0;
for (int i = 1; i <= n; i++) {
if (a[i] % 2 == 0) continue;
if (a[i] == 1) {
cnt++;
if (cnt > 1) {
t[i] = 1;
continue;
}
}
for (int j = 1; j <= n; j++) {
if (a[j] % 2 == 1) continue;
if (isPrime(a[i] + a[j])) {
G[i].push_back(j);
}
}
}
for (int i = 1; i <= n; i++) pei[i] = 0;
int ans = 0;
for (int i = 1; i <= n; i++) {
if (t[i] || a[i] % 2 == 0) continue;
for (int j = 1; j <= n; j++) vis[j] = 0;
if (dfs(i)) ans++;
}
printf("%d\n", n - max(0, cnt - 1) - ans);
return 0;
}
signed main() {
int qq = read();
while (qq--) {
solve();
}
return 0;
}
T2 爆炸
你一个炸弹如果是横向的被炸到那么他自己肯定是考虑纵向去炸,反之亦然。
去考虑对于每个炸弹和它上下左右的第一个炸弹连边。然后就会形成若干个连通块。考虑对每个连通块统计答案然后取其最大值即为答案。
不难发现,一个连通块要么是个环,要么是棵树。考虑如果是环怎么做。不是都说到这儿了你还不会?滚吧。你就 dfs 把他炸弹会炸到哪些行哪些列搞出来,然后一加,因为他会有相交的点会重复计算,暴力统计一下即可,复杂度均摊。那么对于树,无非是环删掉一条边,你就考虑在刚才那个玩意儿的基础上去删掉一行或一列即可。
这傻逼题考试时没写出来就是活该。我就是废物,这傻逼题有啥技术含量写不出来。
#include <bits/stdc++.h>
#define il inline
using namespace std;
const int INF = 0x3f3f3f3f;
const int N = 3000 + 10;
int n, m, kk, bb;
char S[N][N];
int a[N][N];
vector<int> G[N * N];
int tot, id[N][N], ii[N * N], jj[N * N];
il void link(int x, int y) {
G[x].push_back(y);
G[y].push_back(x);
}
int vis[N * N], fl;
vector<int> tmp;
il void dfs(int x, int fr) {
if (vis[x]) {
fl = 1;
return;
}
vis[x] = 1;
tmp.push_back(x);
for (int y : G[x]) {
if (y == fr) continue;
dfs(y, x);
}
}
int ti[N], tj[N], si[N], sj[N];
vector<int> idx, idy;
int main() {
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
cin >> n >> m >> kk >> bb;
for (int i = 1; i <= n; i++) {
cin >> (S[i] + 1);
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (S[i][j] == '.') a[i][j] = 0;
if (S[i][j] == 'k') a[i][j] = 1;
if (S[i][j] == 'b') a[i][j] = 2;
if (a[i][j] == 1) {
si[i] += 1, sj[j] += 1;
}
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (a[i][j] != 2) continue;
id[i][j] = ++tot;
for (int k = i - 1; k >= 1; k--) {
if (a[k][j] == 2) {link(id[k][j], id[i][j]); break;}
}
for (int k = j - 1; k >= 1; k--) {
if (a[i][k] == 2) {link(id[i][k], id[i][j]); break;}
}
ii[id[i][j]] = i, jj[id[i][j]] = j;
}
}
int ans = 0;
for (int x = 1; x <= tot; x++) {
if (vis[x]) continue;
fl = 0;
tmp.clear(), idx.clear(), idy.clear();
dfs(x, 0);
for (int i = 1; i <= n; i++) ti[i] = 0;
for (int j = 1; j <= m; j++) tj[j] = 0;
for (int i : tmp) {
ti[ii[i]]++;
tj[jj[i]]++;
}
int sum = 0;
for (int i = 1; i <= n; i++) {
if (ti[i]) sum += si[i], idx.push_back(i);
}
for (int j = 1; j <= m; j++) {
if (tj[j]) sum += sj[j], idy.push_back(j);
}
for (int i : idx) {
for (int j : idy) {
if (a[i][j] == 1) {
si[i]--, sj[j]--;
sum--;
}
}
}
if (!fl) {
int mx = -INF;
for (int i = 1; i <= n; i++) {
if (ti[i] == 1) mx = max(mx, -si[i]);
}
for (int j = 1; j <= m; j++) {
if (tj[j] == 1) mx = max(mx, -sj[j]);
}
sum += mx;
}
ans = max(ans, sum);
}
cout << ans << "\n";
return 0;
}

浙公网安备 33010602011771号