Codeforsec 1234 记录

目录

  • 综述
  • A Equalize Prices Again
  • B1 / B2 Social Network
  • C Pipes
  • D Distinct Characters Queries
  • E Special Permutations
  • F Yet Another Substring Reverse

综述

CF1234 是一场 Div.3,属于比较简单的比赛。

A Equalize Prices Again

题目描述

\(q\) 组数据。有 \(n\) 个商品,其中第 \(i\) 个商品的成本为 \(a_i\in\mathbb{N^*}\)。把所有的商品按同一个定价(整数)卖出去,问不亏本的定价最低是多少。

解题思路

不会的出门左转,去重修小学数学。

代码

while (q--) {
    cin >> n;
    sum = 0;
    for (int i = 1; i <= n; i++) cin >> a[i], sum += a[i];
    cout << sum / n + ((sum % n) ? 1 : 0) << '\n';
}

B1 / B2 Social Network

题目描述

你将会收到 \(n\) 条短信,但你的手机上只能显示 \(k\) 名发信人信息。第 \(i\) 条短信的发送者是 \(\operatorname{id}_i\)。当你收到第 \(i\) 条短信时,手机将会按以下逻辑处理这条信息:

  • 发信人正在被显示:不改变显示内容。
  • 发信人没被显示,且当前显示的人数小于 \(k\):将发信人信息显示在最上方,其他人的信息下移。
  • 发信人没被显示,且当前显示的人数等于 \(k\):删掉最下方的一条信息,然后按人数小于 \(k\) 的情况处理。

一开始手机上不显示信息。问:手机收到 \(n\) 条消息后会显示什么。

解题思路

直接模拟即可。

代码

#include <bits/stdc++.h>
using namespace std;
int n, k;
map<int, int> vis; // id <= 1e9
queue<int> q; stack<int> s;
int main() {
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    cin >> n >> k;
    for (int i = 1; i <= n; i++) {
        int id;
        cin >> id;
        if (vis[id]) continue;
        vis[id] = 1;
        q.push(id);
        if (q.size() > k) vis[q.front()] = 0, q.pop();
    }
    cout << q.size() << '\n';
    while (q.size()) s.push(q.front()), q.pop();
    while (s.size()) cout << s.top() << ' ', s.pop();
    return 0;
}

C Pipes

题目描述

\(q\) 组数据。有一个 \(2\times n\) 的排水系统,坐标 \((i,j)\) 的水管类型是 \(a_{i,j}\)。下面是六种水管:
image
问:能否通过旋转一些水管,使得水能够从 \((1,1)\) 的左侧流入系统,并从 \((2,n)\) 的右侧流出。能就输出 \(\texttt{YES}\),不能就输出 \(\texttt{NO}\)。下面是一个 \(\texttt{YES}\) 的样例:
image

解题思路

注意到 \(2\) 号水管一定要旋转成 \(1\) 号,因为 \(2\) 号水管显然没有用。而且,\(3,4,5,6\) 四种水管可以看成是同一种:将水从第 \(1\) 行导向第 \(2\) 行或者从第 \(2\) 行导向第 \(1\) 行。所以如果答案是 \(\texttt{YES}\) 则水流的路径是唯一确定的。那么直接模拟水流即可。

代码

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 5;
int n;
int a[3][N], dp[N];
int main() {
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    int _; cin >> _;
    while (_--) {
        cin >> n;
        for (int i = 1; i <= 2; i++) {
            string s;
            cin >> s;
            for (int j = 1; j <= n; j++) {
                a[i][j] = s[j - 1] - '0';
                if (a[i][j] <= 2) a[i][j] = 1;
                else a[i][j] = 3;
            }
        }
        dp[0] = 1;
        for (int i = 1; i <= n; i++) {
            if (a[dp[i - 1]][i] == 1) dp[i] = dp[i - 1];
            else if (a[1][i] != a[2][i]) {
                dp[n] = 0;
                break;
            } else dp[i] = (dp[i - 1] == 1) ? 2 : 1;
        }
        if (dp[n] == 2) cout << "YES\n";
        else cout << "NO\n";
    }
    return 0;
}

D Distinct Characters Queries

题目描述

维护一个长度为 \(n\) 的字符串,支持以下操作:

  • 修改某个位置的字符
  • 区间查询不同的字符个数

\(q\) 次操作。保证序列中的字符都是小写英文字符。\(n,q\le 10^5\)

解题思路

这道题您可以使用自己喜欢的数据结构进行维护。这里提供 \(2\) 套方案:

  • 莫队
  • \(26\) 棵线段树或树状数组

莫队

因为这道题相当于《数颜色 / 维护队列》的弱化版,因此可以使用莫队进行解决。时间复杂度为 \(\mathcal{O}(n^\frac{5}{3})\)

\(26\) 棵线段树或树状数组
也可以维护 \(26\) 棵线段树或树状数组,每棵线段树或树状数组维护一种字母在区间内的出现次数。这样可以通过计算区间内每种字母的数量来计算答案。时间复杂度为 \(\mathcal{O}(kn\log n)\)\(k=26\)

代码

莫队

#include <bits/stdc++.h>
using namespace std;
#define int long long
int block;
const int N = 1500010;
struct query {
	int id, t, l, r;
	bool operator < (const query &b) const {
		if (l / block != b.l / block) {
			return l / block > b.l / block;
		} else if (r / block != b.r / block) {
			return r / block > b.r / block;
		} else {
			return t > b.t;
		}
	}
} q[N];
struct cz {
	int p, x;
} r[N];
int n, m, now, qcnt, rcnt, cnt[N], a[N], ans[N];
void add(int x) {
	if (!cnt[x]) {
		now += 1;
	}
	cnt[x] += 1;
}
void del(int x) {
	cnt[x] -= 1;
	if (!cnt[x]) {
		now -= 1;
	}
}
signed main() {
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    string s;
    cin >> s;
    n = s.size();
    s = "#" + s;
    for (int i = 1; i <= n; i++) a[i] = s[i];
    cin >> m;
	block = pow(n, 0.667);
	for (int i = 1; i <= m; i++) {
        int op, x, y;
        char c;
		cin >> op >> x;
		if (op == 2) {
            cin >> y;
			qcnt++;
            q[qcnt] = {qcnt, rcnt, x, y};
		} else {
            cin >> c;
            while (c < 'a' || c > 'z') cin >> c;
            rcnt++;
			r[rcnt] = {x, c};
		}
	}
	sort(q + 1, q + qcnt + 1);
	int L = 1, R = 0, lst = 0;
	for (int i = 1; i <= qcnt; i++) {
		while (R < q[i].r) {
			add(a[++R]);
		}
		while (L > q[i].l) {
			add(a[--L]);
		}
		while (R > q[i].r) {
			del(a[R--]);
		}
		while (L < q[i].l) {
			del(a[L++]);
		}
		while (lst < q[i].t) {
			lst += 1;
			if (r[lst].p >= L && r[lst].p <= R) {
				add(r[lst].x);
				del(a[r[lst].p]);
			}
			swap(a[r[lst].p], r[lst].x);
		}
		while (lst > q[i].t) {
			if (r[lst].p >= L && r[lst].p <= R) {
				add(r[lst].x);
				del(a[r[lst].p]);
			}
			swap(a[r[lst].p], r[lst].x);
			lst -= 1;
		}
		ans[q[i].id] = now;
	}
	for (int i = 1; i <= qcnt; i++) {
		cout << ans[i] << '\n';
	}
	return 0;
}

\(26\) 棵树状数组

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 100010;
int n, m, a[N], bit[30][N];
int lowbit(int x) {return x & -x;}
void add(int x, int y, int z) {
    while (y <= n) {
        bit[x][y] += z;
        y += lowbit(y);
    }
}
int sum(int x, int y) {
    int res = 0;
    while (y) {
        res += bit[x][y];
        y -= lowbit(y);
    }
    return res;
}
signed main() {
	ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    string s;
    cin >> s;
    n = s.size();
    s = "#" + s;
    for (int i = 1; i <= n; i++) {
        a[i] = s[i] - 'a' + 1;
        add(a[i], i, 1);
    }
    cin >> m;
	for (int i = 1; i <= m; i++) {
        int op, x, y;
        char c;
		cin >> op >> x;
		if (op == 2) {
            cin >> y;
            int ans = 0;
            for (int i = 1; i <= 26; i++) if (sum(i, y) - sum(i, x - 1) > 0) ans++;
            cout << ans << '\n';
		} else {
            cin >> c;
            while (c < 'a' || c > 'z') cin >> c;
            y = c - 'a' + 1;
            add(a[x], x, -1);
            add(y, x, 1);
            a[x] = y;
		}
	}
	return 0;
}

E Special Permutations

题目描述

我们定义 \(p_i(n)\) 为如下排列:\([i, 1, 2, \dots, i - 1, i + 1, \dots, n]\)。也就是说,第 \(i\) 个排列几乎是一个恒等排列(即每个元素都映射到自身),但元素 \(i\) 被放在了第一个位置。例如:

  • \(p_1(4) = [1, 2, 3, 4]\)
  • \(p_2(4) = [2, 1, 3, 4]\)
  • \(p_3(4) = [3, 1, 2, 4]\)
  • \(p_4(4) = [4, 1, 2, 3]\)

给定一个数组 \(x_1, x_2, \dots, x_m\)\(1 \le x_i \le n\))。

\(pos(p, val)\) 表示元素 \(val\) 在排列 \(p\) 中的位置。例如,\(pos(p_1(4), 3) = 3\)\(pos(p_2(4), 2) = 1\)\(pos(p_4(4), 4) = 1\)

定义函数 \(f(p) = \sum\limits_{i=1}^{m - 1} |pos(p, x_i) - pos(p, x_{i + 1})|\),其中 \(|val|\) 表示 \(val\) 的绝对值。该函数表示在排列 \(p\) 中,数组 \(x\) 的相邻元素之间的位置距离之和。

你的任务是计算 \(f(p_1(n)), f(p_2(n)), \dots, f(p_n(n))\)

解题思路

注意到 \(p_{i-1}(n)\)\(p_i(n)\) 的区别在于交换了两个数字的位置。因此求 \(f(p_i(n))\) 时可以在 \(f(p_{i-1}(n))\) 的基础上直接修改这两个数与相邻位置数字的差的绝对值。这样做每个数只被调用 \(\mathcal{O}(1)\) 次。

代码

F Yet Another Substring Reverse

题目描述

给你一个字符串 \(S\),你可以翻转一次 \(S\) 的任意一个子串。问翻转后 \(S\) 的子串中各个字符都不相同的最长子串长度。
\(∣S∣\le10^6\)\(S_i\in\{\texttt{a},\texttt{b},\texttt{c},\dots,\texttt{r},\texttt{s},\texttt{t}\}\)

解题思路

注意到反转相当于把两个字串拼接到一起(忽略字符顺序)。

于是考虑设 \(f_i\) 表示字符集为 \(i\)(状压)且其中的每个字符仅出现一次时,最长的字串长度。此时 \(f_i\) 的取值是 \(0\)(不存在这种字串)或 \(\operatorname{popcount}(i)\)。又设 \(g_i=\max\limits_{j\in i} f_j\),此时答案为 \(\max (f_i+g_{\complement_Ui})\),其中 \(U\) 表示全集。

代码

#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
int n, dp[1048580];
string s;
int main() {
    cin >> s;
    n = s.size();
    for (int i = 0; i < n; i++) {
        for (int j = i, k = 0; j - i < 20 && j < n; j++) {
            int x = s[j] - 'a';
            if (k & (1 << x)) break;
            k |= (1 << x);
            dp[k] = max(dp[k], j - i + 1);
        }
    }
    for (int i = 0; i < 1048576; i++) {
        for (int j = 0; j < 20; j++) {
            if (i & (1 << j)) {
                dp[i] = max(dp[i], dp[i ^ (1 << j)]);
            }
        }
    }
    int ans = 0;
    for (int i = 0; i < 1048576; i++) ans = max(ans, dp[i] + dp[1048575 ^ i]);
    cout << ans << '\n';
    return 0;
}
posted @ 2025-08-30 18:14  cwkapn  阅读(12)  评论(0)    收藏  举报