2025CSP-S模拟赛37 比赛总结
2025CSP-S模拟赛37
| T1 | T2 | T3 | T4 |
|---|---|---|---|
| 100 AC | 5 WA | 25 RE | 0 RE |
总分:130;排名:17/25。
比较失败。T2 本来输出 Alice 有 70 分的(bushi,然后 T3 写了 50 的部分分,然后挂了一半,T4 部分分的 8 分炸了。
T1 新的阶乘
比较简单,按照题意直接模拟即可。
#include <bits/stdc++.h>
#define il inline
using namespace std;
const int N = 1e7 + 10;
int n;
long long t[N];
int f[N], p[N], tot, mn[N];
il void init() {
f[1] = 1;
for (int i = 2; i < N; i++) {
if (!f[i]) {
p[++tot] = i;
mn[i] = i;
}
for (int j = 1; j <= tot; j++) {
if (i * p[j] >= N) break;
f[i * p[j]] = 1;
mn[i * p[j]] = p[j];
if (i % p[j] == 0) break;
}
}
}
int main() {
ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
init();
cin >> n;
int x, y;
for (int i = n, j = 1; i >= 1; i--, j++) {
x = i;
while (x > 1) {
y = mn[x];
while (x % y == 0) {
x /= y;
t[y] += j;
}
}
}
cout << "f(" << n << ")=";
int flag = 0;
for (int i = 1; i <= tot; i++) {
if (t[p[i]]) {
if (flag) cout << "*";
flag = 1;
cout << p[i];
if (t[p[i]] > 1) {
cout << "^" << t[p[i]];
}
}
}
return 0;
}
T2 博弈树
这边直接说结论,就是如果你询问的这个点是直径的中点,那么 Bob 胜,否则 Alice 胜。贴上题解:
我们考虑先手的位置如果在直径端点的话一定是先手必胜的,否则先手一定不能将 点移动到直径端点,于是我们考虑删除了原树所有直径端点的树,如果初始点在这棵树 上为直径端点那么也一定是先手必胜的,因为先手可以将点移动到另一个直径端点,这 样后手就一定会将点移动到原树的直径端点上,并且移动的长度一定小于原树直径,这 样先手就可以将点移动到原树的另一个直径端点取得胜利。 所以这样我们可以将树的叶子一层一层的删下去,如果最后删剩下一个点那么在这 个点是后手必胜的(因为先手无论如何都会将这个点移动到某一层的直径端点),否则 根据我们之前的证明先手一定可以通过不断将点移动到下一层的直径端点取得胜利。
#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 = 1e5 + 10;
int n, qq;
vector<int> G[N];
int u, v, dep[N];
il void dfs(int x, int fa) {
dep[x] = dep[fa] + 1;
if (dep[x] > dep[u]) u = x;
for (int y : G[x]) {
if (y == fa) continue;
dfs(y, x);
}
}
int f[N];
il void dfs1(int x, int fa) {
dep[x] = dep[fa] + 1;
f[x] = fa;
if (dep[x] > dep[v]) v = x;
for (int y : G[x]) {
if (y == fa) continue;
dfs1(y, x);
}
}
int main() {
n = read(), qq = read();
for (int i = 1; i < n; i++) {
int x = read(), y = read();
G[x].push_back(y);
G[y].push_back(x);
}
dfs(1, 0);
dfs1(u, 0);
int len = dep[v], mid = 0;
if (len % 2 == 1) {
mid = v;
for (int i = 1, x = v; i * 2 < len; i++, x = f[x]) {
mid = f[mid];
}
}
while (qq--) {
int x = read();
if (x == mid) printf("Bob\n");
else printf("Alice\n");
}
return 0;
}
T3 划分
首先,如果前 \(k\) 位都是 0,那么显然是找到第一个不是 0 的位置然后把他及之后的位置作为一段,然后把前面的那么多个 0 分成 \(k-1\) 段最优。
现在考虑前缀不足 \(k\) 个 0 的情况。答案划分一定是这么一个形态,一段长为 \(n-k+1\) 的加上一堆长为 1 的段。那么现在你就找到最大的一段 \(n-k+1\) 的数然后加上其他的 1 就行了。然后你发现这些对于所有最大的划分方案,他的 \(n-k+1\) 的那一段的前 \(n-k\) 位一定相同。那你现在去扫一遍记录一个当前最大的,然后对于每个段你二分哈希去比较两段的大小即可。
#include <bits/stdc++.h>
#define il inline
#define int long long
#define ull unsigned long long
using namespace std;
const int P = 131;
const int MOD = 998244353;
const int N = 2e6 + 10;
int n, kk, a[N];
char S[N];
int fact[N], ny[N];
il int fpow(int a, int x) {
a %= MOD;
int ans = 1;
while (x) {
if (x & 1) ans = ans * a % MOD;
a = a * a % MOD;
x >>= 1;
}
return ans;
}
il int C(int n, int m) {
return n < m ? 0 : fact[n] * ny[m] % MOD * ny[n - m] % MOD;
}
int s[N];
ull p[N], hsh[N];
il ull gethsh(int l, int r) {
return hsh[r] - hsh[l - 1] * p[r - l + 1];
}
signed main() {
ios::sync_with_stdio(0); cin.tie(0), cout.tie(0);
cin >> n >> kk >> (S + 1);
for (int i = 1; i <= n; i++) a[i] = (S[i] == '1');
if (n == kk) {
int ans = 0;
for (int i = 1; i <= n; i++) ans += a[i];
printf("%lld 1\n", ans);
return 0;
}
fact[0] = 1;
for (int i = 1; i <= n; i++) fact[i] = fact[i - 1] * i % MOD;
ny[n] = fpow(fact[n], MOD - 2);
for (int i = n - 1; i >= 0; i--) ny[i] = ny[i + 1] * (i + 1) % MOD;
int id = 0;
while (id < n && a[id + 1] == 0) id++;
if (id >= kk) {
int ans = 0;
for (int i = id + 1; i <= n; i++) {
ans = (ans * 2 % MOD + a[i]) % MOD;
}
int num = 0;
for (int i = kk; i <= id + 1; i++) {
num = (num + C(id, i - 1)) % MOD;
}
printf("%lld %lld\n", ans, num);
return 0;
}
for (int i = 1; i <= n; i++) {
s[i] = (s[i - 1] + a[i]) % MOD;
}
id++;
p[0] = 1;
for (int i = 1; i <= n; i++) p[i] = p[i - 1] * P;
for (int i = 1; i <= n; i++) hsh[i] = hsh[i - 1] * P + a[i];
int len = n - kk + 1;
int mxid = id, num = 1;
for (int i = id + 1; i + len - 1 <= n; i++) {
if (i == 0) continue;
int L = 0, R = len - 1, res = 0;
while (L <= R) {
int mid = L + R >> 1;
if (gethsh(mxid, mxid + mid - 1) == gethsh(i, i + mid - 1)) {
res = mid;
L = mid + 1;
} else {
R = mid - 1;
}
}
if (res == len - 1) {
num = (num + 1) % MOD;
} else if (a[i + res] > a[mxid + res]) {
mxid = i;
num = 1;
}
}
int ans = 0;
for (int i = mxid; i <= mxid + len - 1; i++) {
ans = (ans * 2 % MOD + a[i] % MOD);
}
ans = (ans + s[mxid - 1] + s[n] - s[mxid + len - 1]) % MOD;
printf("%lld %lld\n", ans, num);
return 0;
}
T4 灯笼
比较巧妙的一个 dp。
成天搬人博客也没意思,那这里也不写题解了,这边推荐阅读 题解:P9312 [EGOI 2021] Lanterns / 灯笼
#include <bits/stdc++.h>
#define il inline
#define int long long
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 INF = 0x3f3f3f3f3f3f3f3f;
const int N = 2005;
int n, kk, h[N];
int p[N], c[N], a[N], b[N];
int id1[N], id2[N];
il bool cmp1(int x, int y) {return a[x] < a[y];}
il bool cmp2(int x, int y) {return b[x] > b[y];}
int la[N], ra[N], lb[N], rb[N];
int f[N][N];
il bool check(int x, int y, int k) {
return la[x] < p[k] && p[k] < ra[x] && lb[y] < p[k] && p[k] < rb[y] && b[k] >= a[x] && a[k] <= b[y];
}
struct node {
int k, f;
il bool operator < (const node & cmp) const {
return f > cmp.f;
}
};
priority_queue<node> qx[N], qy[N];
signed main() {
n = read(), kk = read();
for (int i = 1; i <= n; i++) h[i] = read();
for (int i = 1; i <= kk; i++) {
p[i] = read(), c[i] = read(), a[i] = read(), b[i] = read();
id1[i] = id2[i] = i;
}
sort(id1 + 1, id1 + 1 + kk, cmp1);
sort(id2 + 1, id2 + 1 + kk, cmp2);
for (int i = 1; i <= kk; i++) {
la[i] = p[i]; while (la[i] >= 1 && h[la[i]] >= a[i]) la[i]--;
ra[i] = p[i]; while (ra[i] <= n && h[ra[i]] >= a[i]) ra[i]++;
lb[i] = p[i]; while (lb[i] >= 1 && h[lb[i]] <= b[i]) lb[i]--;
rb[i] = p[i]; while (rb[i] <= n && h[rb[i]] <= b[i]) rb[i]++;
}
for (int i = 1; i <= kk; i++)
for (int j = 1; j <= kk; j++) f[i][j] = INF;
for (int i = 1; i <= kk && a[id1[i]] == a[id1[1]]; i++) {
for (int j = 1; j <= kk && b[id2[j]] == b[id2[1]]; j++) {
f[id1[i]][id2[j]] = 0;
}
}
for (int i = 1; i <= kk; i++) {
for (int j = 1; j <= kk; j++) {
int x = id1[i], y = id2[j];
if (la[x] >= p[y] || p[y] >= ra[x] || lb[y] >= p[x] || p[x] >= rb[y]) continue;
if (a[y] < a[x] && b[x] > b[y]) continue;
if (a[y] < a[x]) f[x][y] = min(f[x][y], f[y][y]);
if (b[x] > b[y]) f[x][y] = min(f[x][y], f[x][x]);
while (!qy[y].empty() && !check(x, y, qy[y].top().k)) qy[y].pop();
if (!qy[y].empty()) f[x][y] = min(f[x][y], qy[y].top().f);
while (!qx[x].empty() && !check(x, y, qx[x].top().k)) qx[x].pop();
if (!qx[x].empty()) f[x][y] = min(f[x][y], qx[x].top().f);
qx[x].push({y, f[x][y] + c[y]});
qy[y].push({x, f[x][y] + c[x]});
}
}
for (int i = 1; i <= kk; i++) {
printf("%lld\n", f[i][i] < INF ? f[i][i] + c[i] : -1);
}
return 0;
}

浙公网安备 33010602011771号