T3
一道经典经典的计数题目。
我们考虑如果没有进行交换的总方案数。
因为要求子序列是 xyz,我们先从 y 开始考虑。我们假设这个 y 前面有 \(cnt1\) 个x,后面有 \(cnt2\) 个,根据乘法原理,我们很容易知道这个 y 的贡献值是 \(cnt1 \times cnt2\),然后再把每一个y的贡献加起来即可。
我们使用 \(pre_i\) 表示第i个位置前面有多少个 x,\(suf_i\) 表示第i个位置后面有多少个 z,使用 \(O(n)\) 的前缀和、后缀和即可。
设 \(sum\) 为此时的答案。
现在考虑交换。
我们发现,一共会有 \(3 * 3 = 9\) 个不同的 \(a_x\) 与 \(a_{x + 1}\)。
但是,如果 \(a_x\) 和 \(a_{x + 1}\) 都不是 y,那么根本没有影响,所以答案还是原来的;
而且,如果 \(a_x = a_{x + 1}\) 的话,那么还是没有影响;
接下来,进行分讨:
-
假设 \(a_x = x\),\(a_{x + 1} = y\)。此时,我们考虑交换之后会有什么影响。很明显,现在的序列是 yx,那么,原来的 \(x + 1\) 的后面的所有的 z 都会减少一个可与匹配的 x,所以 \(sum\) 应该减掉 \(suf_{x + 1}\)。
-
假设 \(a_x = z\),\(a_{x + 1} = y\)。类比着上面的 ,我们很容易发现,\(sum\) 应该加上 \(pre_{x + 1}\)。
-
假设 \(a_x = y\),\(a_{x + 1} = x\)。类比着上面的 ,我们很容易发现,\(sum\) 应该加上 \(suf_x\)。
-
假设 \(a_x = y\),\(a_{x + 1} = z\)。类比着上面的 ,我们很容易发现,\(sum\) 应该减掉 \(pre_x\)。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;const int M = 1e6 + 10;
char a[M];
ll n, suf[M][2], pre[M][2], ans, sum, q;int main() {// freopen("decipher.in", "r", stdin);// freopen("decipher.out", "w", stdout);cin >> n >> q;for (int i = 1; i <= n; i ++) cin >> a[i];for (int i = 1; i <= n; i ++) {pre[i][0] = pre[i - 1][0], pre[i][1] = pre[i - 1][1];if (a[i] == 'x') pre[i][0] ++;if (a[i] == 'z') pre[i][1] ++;}for (int i = n; i >= 1; i --) {suf[i][0] = suf[i + 1][0], suf[i][1] = suf[i + 1][1];if (a[i] == 'x') suf[i][0] ++;if (a[i] == 'z') suf[i][1] ++;}for (int i = 1; i <= n; i ++) {if (a[i] == 'y') sum += pre[i][0] * suf[i][1];}// cout << "sum = " << sum << "\n";while (q --) {int x; cin >> x;if (a[x] == 'y' && a[x + 1] == 'x') {sum += suf[x][1];pre[x][0] ++;}if (a[x] == 'y' && a[x + 1] == 'z') {sum -= pre[x][0];suf[x][1] --;}if (a[x] == 'x' && a[x + 1] == 'y') {sum -= suf[x + 1][1];pre[x + 1][0] --;}if (a[x] == 'z' && a[x + 1] == 'y') {sum += pre[x + 1][0];suf[x + 1][1] ++;}ans ^= sum;swap(a[x], a[x + 1]);swap(pre[x][0], pre[x + 1][0]);swap(pre[x][1], pre[x + 1][1]);swap(suf[x][0], suf[x + 1][0]);swap(suf[x][1], suf[x + 1][1]);}cout << ans << "\n";return 0;
}
文本难度:CSP-J T2
思维难度:CSP-J T3/CSP-S T1-T1.5
代码实现难度:CSP-J T3
T4
先说一句题外话,本题改编自P11969,是笔者在 luogu 月赛场切的一道题目,同时也是笔者在 luogu 上通过的第一篇题解。
我们先观察性质。
首先,先手一定不会选择合并操作,假如你看出了这个性质你就超过了 jmx 学长,同理,后手一定不会选择分裂操作。
同时,通过阅读题目,我们知道这里的字典序不是传统意义上的字典序,这很有利于简化题目难度。
接下来,对于 \(T\) 的奇偶性。
-
假设 \(T\) 是奇数,也就是说先手最后一次操作。我们想一下,因为操作 \(1\) 和 操作 \(2\) 是可以来回转换的,所以先手先进行了一次最优操作,后手再干回去,以此类推。那么,我们分别考虑先手和后手的操作最优性。
先手的希望是把字典序尽可能变小,也就是说让 \(1\) 尽可能在前面。形式化的,我们找到第一个 \(i\),使得 \(a_i > 1\),然后把 \(i\) 拆分成 \(1\) 和 \(a_i - 1\) 即可。
接下来考虑后手。很明显,后手的最优策略就是把 \(a_1\) 替换成 \(a_1 + a_2\)。 -
假设 \(T\) 是偶数。也就是说,是对方最后一次操作。
假设后手最后一次操作,先手肯定把序列变成了像上面这个情况,接下来考虑后手。和上面的一样,后手的最优策略肯定是把 \(a_1\) 变成 \(a_1 + a_2\)。
假设先手最后一次操作,先手肯定是把第 \(1\) 个数字拆成了 \(1\) 和 \(a_i - 1\)。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;const int M = 1e5 + 10;
ll n, a[M], T;void work1() {bool flag = 0, flag2 = 0;for (int i = 1; i <= n; i ++) {if (a[i] != 1) {flag = 1;break ;}}cout << n + flag << "\n";for (int i = 1; i <= n; i ++) {if (a[i] != 1 && flag2 == 0) {cout << 1 << " " << a[i] - 1 << " ";flag2 = 1;} else {cout << a[i] << " ";}}cout << "\n";if (n == 1) {cout << a[1] << "\n";return ;}cout << n - 1 << "\n";for (int i = 2; i <= n; i ++) {if (i == 2) cout << a[1] + a[2] << " ";else cout << a[i] << " ";} cout << "\n";
}void work2() {bool flag2 = 0;int now = 0;vector < int > b(n + 2);for (int i = 1; i <= n; i ++) {if (a[i] != 1 && flag2 == 0) {b[++ now] = 1; b[++ now] = a[i] - 1;flag2 = 1;} else {b[++ now] = a[i];}}if (now == 1) {cout << now << "\n";cout << b[1] << "\n";} else {cout << now - 1 << "\n";for (int i = 2; i <= now; i ++) {if (i == 2) cout << b[1] + b[2] << " ";else cout << b[i] << " ";} cout << "\n";}if (n == 1) {if (a[1] == 1) {cout << n << "\n";cout << "1\n";return ; } else {cout << 2 << "\n";cout << 1 << " " << a[1] - 1 << "\n";}} else {cout << n << "\n";int t = a[1] + a[2];a[2] = t - 1;cout << 1 << " ";for (int i = 2; i <= n; i ++) cout << a[i] << " ";cout << "\n";}
}int main() {// freopen("emperor.in", "r", stdin);
// freopen("emperor.out", "w", stdout);cin >> T >> n;for (int i = 1; i <= n; i ++) cin >> a[i];if (T % 2 == 1) work1();else work2();return 0;
}
文本难度:CSP-J T3.5-T4/CSP-S T2-T2.5
思维难度:CSP-J T3.5-T4/CSP-S T2-T2.5
代码难度:CSP-J T4/CSP-S T2.5