2025河南萌新赛第七场比赛记录
solved 8 problems
A、C、D、I、J、K、L、M: @dbwysc
solved 8 problems
C、D、E、I、J、K、L、M: @xmjlove
M.奥数班
思路
签到题,直接 if else 就行,但是形如 “如果B比A大十倍及以上” 指的是 \(\geq\) 而非 \(>\)
代码
void solve(void) {
i64 a, b; std::cin >> a >> b;
if(a >= 10 * b) {
std::cout << ">>";
}
else if(a > b) {
std::cout << ">";
}
else if(a == b) {
std::cout << "=";
}
else if(a * 10 <= b ) {
std::cout << "<<";
}
else if(a < b) {
std::cout << "<";
}
}
J.掷骰子
思路
本题可以直接暴力。但是注意到骰子 \(A\) 的六个面上的数字均为 \(2 + 5k\) ,骰子 \(B\) 的六个面上的数字均为 \(3 + 5k\) ,因此 \(A、B\) 个投掷九次的结果和一定是 \(5\) 的倍数。同时取的最小可能 \(45\) 和 最大 \(495\) 。只要满足答案在这个区间内并且是五的倍数即可。
代码
void solve(void) {
int n; std::cin >> n;
int ans = 0;
while(n--) {
int x; std::cin >> x;
if(x < 45 || x > 495 || x % 5) ans++;
}
std::cout << ans;
}
L.打游戏 or 写程序
思路
设 \(f_i\) 为完成 \(i\) 件事情的方案数,有 \(f_0 = 1\) 。接下来考虑状态转移:
如果 \(i \leq k\) ,说明这 \(i\) 件事之间无法插入打游戏,因此 \(f_i = f_{i - 1} + 1\) ;
否则想要完成第 \(i\) 件事可以由 \(f_{i - 1}\) 和 \(f_{i - k + 1}\) 转移到。
因此状态转移方程为:
代码
void solve(void) {
int n, k; std::cin >> n >> k;
std::vector<int> f(1e6 + 10);
f[0] = 1;
for(int i = 1; i <= n; i++) {
if(i <= k) f[i] = f[i - 1] + 1;
else f[i] = f[i - 1] + f[i - k - 1];
f[i] %= P;
}
std::cout << f[n];
}
C. 灯光是我在异世界的呼喊
思路
拿 trie 维护一棵前缀树,最后求一下哪个前缀出现的次数最多即可。
代码
int son[N][26], cnt[N], idx;
int pre[N];
void insert(std::string s) {
int p = 0;
for(int i = 0; i < s.size(); i++) {
int u = s[i] - 'a';
if(!son[p][u]) son[p][u] = ++idx;
p = son[p][u];
pre[p]++;
}
cnt[p]++;
}
void solve(void) {
int n; std::cin >> n;
for(int i = 1; i <= n; i++) {
std::string s;
std::cin >> s;
insert(s);
}
int ans = 0;
for(int i = 1; i <= idx; i++) {
if(cnt[i]) ans = std::max(ans, pre[i]);
}
std::cout << ans << '\n';
}
K.好奇的小夏
思路
双指针 + 前缀和。
先将原数组排序,然后维护前缀和。再用双指针维护一个滑动窗口,对于每个窗口,计算需要多少次操作能将所有数字变为右端点,如果这个需求超出了 \(k\) 次限制,左端点移动缩小窗口,否则右端点移动扩大窗口。在这个过程中记录最大值。
代码
void solve(void) {
int n, k; std::cin >> n >> k;
std::vector<i64> a(n + 1), s(n + 1);
for(int i = 1; i <= n; i++) std::cin >> a[i];
std::sort(a.begin() + 1, a.end());
for(int i = 1; i <= n; i++) {
s[i] = s[i - 1] + a[i];
}
int l = 1, cnt = 1, val = a[1];
for(int r = 1; r <= n; r++) {
i64 cost = a[r] * (r - l + 1) - (s[r] - s[l - 1]);
while(cost > k) {
l++;
cost = a[r] * (r - l + 1) - (s[r] - s[l - 1]);
}
if(r - l + 1 > cnt)
cnt = r - l + 1, val = a[r];
}
std::cout << cnt << " " << val;
}
A. 数组的贡献
思路
将问题转化为计算每个 \(a_i\) 左边和右边小于它的个数,记作 \(L_i\) 和 \(R_i\) 最后的答案就变成了
维护 \(L\) 和 \(R\) 的过程可以用 RMQ,这里用了线段树。
代码
struct Info {
i64 sum;
int len;
Info(): sum(0), len(0) {}
Info(i64 x): sum(x), len(1) {}
Info(i64 s, int l): sum(s), len(l) {}
friend Info operator+(const Info &A, const Info &B) {
return Info(A.sum + B.sum, A.len + B.len);
}
};
struct SegmentTree {
int n;
std::vector<i64> tag;
std::vector<Info> info;
SegmentTree(int n_) : n(n_), tag(4 * n), info(4 * n) {};
void pull(int p) {
info[p] = info[2 * p] + info[2 * p + 1];
}
void add(int p, i64 v) {
tag[p] += v;
info[p].sum += v * info[p].len;
}
void push(int p) {
add(2 * p, tag[p]);
add(2 * p + 1, tag[p]);
tag[p] = 0;
}
void build(int p, int l, int r, const std::vector<i64> &a) {
if(r - l == 1) {
info[p] = Info(a[l]);
return;
}
int m = (l + r) / 2;
build(2 * p, l, m, a);
build(2 * p + 1, m, r, a);
pull(p);
}
void build(const std::vector<i64> &a) {
build(1, 0, n, a);
}
Info query(int p, int l, int r, int x, int y) {
if(l >= y || r <= x) {
return {};
}
if(l >= x && r <= y) {
return info[p];
}
int m = (l + r) / 2;
push(p);
return query(2 * p, l, m, x, y) + query(2 * p + 1, m, r, x, y);
}
Info query(int x, int y) {
return query(1, 0, n, x, y);
}
void rangeAdd(int p, int l, int r, int x, int y, i64 v) {
if(l >= y || r <= x) return;
if(l >= x && r <= y) return add(p, v);
int m = (l + r) / 2;
push(p);
rangeAdd(2 * p, l, m, x, y, v);
rangeAdd(2 * p + 1, m, r, x, y, v);
pull(p);
}
void rangeAdd(int x, int y, i64 v) {
rangeAdd(1, 0, n, x, y, v);
}
void modify(int p, int l, int r, int x, const Info &v) {
if(r - l == 1) {
info[p] = v;
return;
}
int m = (l + r) / 2;
push(p);
if(x < m) {
modify(2 * p, l, m, x, v);
} else {
modify(2 * p + 1, m, r, x, v);
}
pull(p);
}
void modify(int x, const Info &v) {
modify(1, 0, n, x, v);
}
};
void solve(void) {
int n; std::cin >> n;
std::vector<i64> a(n + 1);
std::vector<i64> L(n + 1), R(n + 1);
for(int i = 1; i <= n; i++) std::cin >> a[i];
std::vector<i64> tmp = a;
std::sort(tmp.begin() + 1, tmp.end());
auto ed = std::unique(tmp.begin() + 1, tmp.end());
for(int i = 1; i <= n; i++) {
auto it = std::lower_bound(tmp.begin() + 1, ed, a[i]);
a[i] = (int)(it - (tmp.begin() + 1));
}
int M = (int)(ed - (tmp.begin() + 1));
std::vector<i64> init(M, 0);
SegmentTree segtree(M);
segtree.build(init);
for(int i = 1; i <= n; i++) {
int x = a[i];
i64 cnt = segtree.query(0, x + 1).sum;
L[i] = cnt + 1;
segtree.rangeAdd(x, x + 1, 1);
}
segtree.build(init);
for(int i = n; i >= 1; i--) {
int x = a[i];
i64 cnt = segtree.query(0, x + 1).sum;
R[i] = cnt + 1;
segtree.rangeAdd(x, x + 1, 1);
}
i64 ans = 0;
for(int i = 1; i<= n; i++) ans = (ans + L[i] * R[i]) % P;
std::cout << ans << '\n';
}
D.灯泡
思路
本题为@xmjlove的思路
注意到每个对答案有贡献的数字要么是某个数的平方,要么是某个数的平方的两倍。
代码
public static void main(String[] args) throws Exception {
long n = Long.parseLong(br.readLine());
pw.println((long)(Math.sqrt(n) + (long)Math.sqrt(n / 2));
pw.flush();
}
I.Raidian 的游戏
思路
注意到 \(n \leq 8\) ,可以把 \(n\) 选 \(m\) 的每一种排列都选出来,然后与题目中的条件一一判断,如果存在满足所有条件的排列就是答案,否则无解。
代码
void solve(void) {
int n, m, k; std::cin >> n >> m >> k;
std::vector<std::vector<int>> q(k, std::vector<int>(m + 1));
for (int i = 0; i < k; i++) {
for (int j = 0; j < m; j++) {
std::cin >> q[i][j];
}
std::cin >> q[i][m];
}
std::vector<int> st(n + 1);
std::vector<std::vector<int>> premutitaion;
std::vector<int> b;
auto dfs = [&](auto &&self, int step) -> void {
if (step == m) {
premutitaion.push_back(b);
return;
}
for (int i = 1; i <= n; i++) {
if (!st[i]) {
st[i] = 1;
b.push_back(i);
self(self, step + 1);
st[i] = 0;
b.pop_back();
}
}
};
dfs(dfs, 0);
int ansidx = 0;
int cnt1 = 0;
for (int i = 0; i < premutitaion.size(); i++) {
int flag = 1;
for (int j = 0; j < k; j++) {
int cnt2 = 0;
for (int z = 0; z < m; z++) {
if (premutitaion[i][z] == q[j][z])
cnt2++;
}
if (cnt2 != q[j][m]) {
flag = 0;
break;
}
}
if (flag == 1) {
cnt1++;
ansidx = i;
}
}
if (cnt1 != 1) {
std::cout << "-1\n";
return;
}
for (int i = 0; i < m; i++)
std::cout << premutitaion[ansidx][i] << " ";
}
E. 优美子序列
思路
本题为@xmjlove的思路
待补充
代码
public static void main(String[] args) throws Exception {
int n = Integer.parseInt(br.readLine());
String s = br.readLine();
int[][] dp = new int[27][27];
dp[0][s.charAt(0) - 'a' + 1] = 1;
for (int i = 1; i < n; i++) {
int[][] temp = new int[27][27];
for (int j = 0; j < 27; j++) {
temp[j] = Arrays.copyOf(dp[j], 27);
}
int idx = s.charAt(i) - 'a' + 1;
temp[0][idx] = 1;
for (int j = 1; j < 27; j++) {
if (j == idx)
continue;
for (int k = 0; k < 27; k++) {
if (k == idx)
continue;
if(dp[k][j] != 0)
temp[j][idx] = Math.max(temp[j][idx], dp[k][j] + 1);
}
//pw.println(i + " " + ((char)('a' + j - 1)) + "" + ((char)('a' + idx - 1)) + " " + temp[j][idx]);
}
for (int j = 0; j < 27; j++) {
dp[j] = Arrays.copyOf(temp[j], 27);
}
}
int ans = 0;
for (int i = 0; i < 27; i++) {
for (int j = 0; j < 27; j++) {
ans = Math.max(ans, dp[i][j]);
}
}
pw.println(ans);
pw.flush();
}

浙公网安备 33010602011771号