Edu174

A Was there an Array?

1
观察题目发现如果给定的B[i] = 1, 左右都得是相等的, 如果出现了连续三个数字( i - 1, i , i + 1 ) 对应是 1 0 1,则有 a[i - 1] = a[ i ], a[ i ] = a[i + 1],  得出 a[i - 1] = a[i + 1], 但是i == 0 又推出 a[i - 1] != a[i + 1]矛盾,所以只需要判断一下是否出现了这种结构即可
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include<bits/stdc++.h>
using namespace std;

using i64 = long long;

void solve() {
int n;
cin >> n;

vector<int> a(n + 1), b(n + 1);
for (int i = 2; i < n; i++) {
cin >> b[i];
}

for (int i = 2; i < n; i++) {
if (b[i] == 0 && b[i - 1] == 1 && b[i + 1] == 1) {
cout << "No\n";
return;
}
}
cout << "Yes\n";
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);

int t;
cin >> t;

while(t--) {
solve();
}

return 0;
}

B. Set of Strangers

观察到如果出现有相邻的相同数字,则对应数字需要操作两次才能变成另外一个数字,如果没有出现相邻,那么同一种数字总是需要1次即可完成操作,

暴力统计每个数字需要的操作次数,取最小的 n - 1 次累计求和就是答案。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#include<bits/stdc++.h>
using namespace std;

using i64 = long long;
int way[4][2] = { 1, 0, -1, 0, 0, 1, 0, -1 };
void solve() {
int n, m;
cin >> n >> m;

vector a(n + 1, vector<int> (m + 1)), vis(n + 1, vector<int> (m + 1));
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
cin >> a[i][j];
}
}

map<int, int> mp;
bool f = 0;
auto dfs = [&](auto self, int x, int y, int g) -> void {
vis[x][y] = 1;
for (int i = 0; i < 4; i++) {
int dx = x + way[i][0];
int dy = y + way[i][1];
if (dx <= 0 || dy <= 0 || dx > n || dy > m || vis[dx][dy] || a[dx][dy] != g) continue;
f = 1;
self(self, dx, dy, g);
}
};

for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (!vis[i][j]) {
f = 0;
dfs(dfs, i, j, a[i][j]);
if (f) {
mp[a[i][j]] = 2;
} else {
if (!mp.count(a[i][j])) {
mp[a[i][j]] = 1;
}
}
}
}
}

int ans = 0;
vector<int> b;
for (auto [x, y] : mp) {
b.push_back(y);
}
sort(b.begin(), b.end());
for (int i = 0; i < b.size() - 1; i++) {
ans += b[i];
}
cout << ans << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);

int t;
cin >> t;

while(t--) {
solve();
}

return 0;
}

C. Beautiful Sequence

我的思路 :

题目要求Beautiful序列满足以下条件:

  1. 左侧条件:除第一个元素外,每个元素左侧存在一个比它小的元素。
  2. 右侧条件:除最后一个元素外,每个元素右侧存在一个比它大的元素。

当元素只能取1、2、3时,序列结构必为 [1, 2, ..., 2, 3],原因如下:

首元素必须为1

  • 若首元素为2或3,后续元素无法满足左侧条件(如首元素为2时只能接3,但长度不足;首元素为3时无后续元素)。

末元素必须为3

  • 若末元素非3(如1或2),倒数第二个元素无法满足右侧条件(如末元素为2时,右侧无更大元素)。

中间元素必须全为2

  • 中间元素不能为1(左侧无更小元素)或3(右侧无更大元素),因此只能由2构成,且至少包含一个2(长度≥3)。

综上,Beautiful序列的结构必为:

[ 1 2 2 2 … 3 ]


统计所有满足条件的子序列: 1. 选择一个1的位置`i`作为首元素。 2. 选择一个3的位置`j`(`j > i`)作为末元素。 3. 中间区间`(i, j)`中至少有一个2,且所有元素为2。 设区间`(i, j)`中有`c`个2,则中间部分的子序列组合数为: $$ 2^c - 1 $$ 总答案为所有`(i, j)`对的组合数之和: $$ \text{ans} = \sum_{\substack{i: a[i]=1}} \sum_{\substack{j>i: a[j]=3}} \left(2^{\text{\#}\{k \in (i,j): a[k]=2\}} - 1\right) $$ --- ### 前缀和数组 定义前缀和数组`P`,其中: $$ P[i] = \text{数组前}i\text{个元素中2的个数} $$ 区间`(i, j)`中2的数量为: $$ P[j-1] - P[i] $$ ### 优化内层求和 将内层求和转换为线性遍历: - 对于每个位置`j`(`a[j]=3`),计算`X = P[j-1]`。 - 累加所有`i < j`且`a[i]=1`的贡献: $$ \sum_{\substack{i: a[i]=1 \\ i < j}} \left(2^{X - P[i]} - 1\right) $$ 通过分解公式: $$ 2^{X - P[i]} = 2^X \cdot 2^{-P[i]} $$ 定义累加量: - `sumInv`:所有左侧1对应的`2^{-P[i]`之和。 - `cntOne`:左侧1的个数。 则当前`j`的贡献为: $$ 2^X \cdot \text{sumInv} - \text{cntOne} $$ ### 实现步骤 1. **遍历数组**,维护前缀和`P`。 2. **遇到1时**:更新`cntOne`和`sumInv += 2^{-P[i]}`。 3. **遇到3时**:根据当前`X = P[j-1]`计算贡献并累加。 ---

总结

结构限制

  • Beautiful序列严格遵循[1, 2, ..., 2, 3]形式,中间至少一个2。

计数公式

  • 每个合法的(i, j)对贡献为2^{中间2的个数} - 1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
const int mod = 998244353;

template<class T>
constexpr T power(T a, i64 b, T mod) {
T res = 1;
for (; b; b /= 2, a = a * a % mod) {
if (b % 2) {
res = (res * a) % mod;
}
}
return res;
}

const int N = 200005;
vector<i64> pow2(N), inv(N);

i64 inv2 = power<i64>(2, mod - 2, mod);

void solve() {
int n;
cin >> n;

vector<int> a(n + 1);
for (int i = 1; i <= n; i++) {
cin >> a[i];
}

vector<int> P(n + 1);
for (int i = 1; i <= n; i++) {
P[i] = P[i - 1] + (a[i] == 2);
}

i64 one = 0, sum = 0, ans = 0;
for (int j = 1; j <= n; j++) {
if (a[j] == 1) {
one = (one + 1) % mod;
sum = (sum + inv[P[j]]) % mod;
} else if (a[j] == 2) {

} else {
int X = P[j - 1];
i64 g = ((pow2[X] * sum) % mod - one) % mod;
if (g < 0) g += mod;
ans = (ans + g) % mod;
}
}
cout << ans % mod << "\n";
}

int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);

int t;
cin >> t;

pow2[0] = 1;
for (int i = 1; i < N; i++) {
pow2[i] = (pow2[i - 1] * 2) % mod;
}

inv[0] = 1;
for (int i = 1; i < N; i++) {
inv[i] = (inv[i - 1] * inv2) % mod;
}

while (t--) {
solve();
}
return 0;
}

题解dp思路 :
等我看完了再update

D. Palindrome Shuffle

首先知道前后本来就回文的字母可以先移除,所以只需要处理一个前后两个字母不一样的字符串即可,那么问题就变成处理一个字符串S, 只能重新排列S的某个前缀或者后缀子串使得它变成Palindrome

来看样例4 :

1
acbacddacbca

可以看出需要再4 - 5进行操作,那么也可以在 4 - 6, 4 - 7 等等长度大于他的子串(只需要包含他)进行操作,即长度具有单调性,可以使用二分答案。

我们处理从前开始翻转情况,那么从后开始翻转情况可以reverse后套用从前面开始的情况进行操作。

check函数

首先统计待处理字符串中每个字母出现的次数,之后分两种情况 :

  • 情况一 : L(左字符串起始位置) + len - 1 <= n / 2
    此时和样例4一样,需要在len范围内前缀和后缀里面的所有字符数量相同,同时保证没操作到的字符本来就是回文的(可以多开一个数组预处理记录,看代码is, ned部分)

    • 情况二 : L(左字符串起始位置) + len - 1 > n / 2
      只需要左边的每个字符数量全部大于或等于右边剩下的即可。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
#include<bits/stdc++.h>
using namespace std;

using i64 = long long;

void solve() {
string s;
cin >> s;

s = ' ' + s;

int n = s.size() - 1;
int l = 1, r = n;
while (s[l] == s[r] && l <= r) {
l++, r--;
}

if (l > r) {
cout << 0 << '\n';
return;
}

vector<int> c(27 + 1);
for (int i = l; i <= r; i++) {
c[s[i] - 'a']++;
}

vector<int> is(n + 1), ned(n + 1);
for (int i = 1; i <= n / 2; i++) {
if (s[i] != s[n - i + 1]) {
is[i] = 1;
}
}

for (int i = n / 2 - 1; i >= 1; i--) {
if (is[i + 1] == 1 || ned[i + 1]) {
ned[i] = 1;
}
}

auto ck = [&](int len) -> bool {
vector<int> g(28);
for (int i = l; i <= l + len - 1; i++) {
g[s[i] - 'a']++;
}

if (l + len - 1 <= n / 2) {
for (int i = r; i >= r - len + 1; i--) {
g[s[i] - 'a']--;
}

for (int i = 0; i < 26; i++) {
if (g[i] != 0) {
return false;
}
}

if (ned[l + len - 1]) {
return false;
}
return true;
} else {
for (int i = 0; i < 26; i++) {
if (c[i] - g[i] > g[i]) {
//cout << len << ' ' << char(i + 'a') << '\n';
return false;
}
}
return true;
}
};

int lo = 2, ri = r - l;
while (lo <= ri) {
int mid = (lo + ri) >> 1;
if (ck(mid)) {
ri = mid - 1;
} else {
lo = mid + 1;
}
}
int ans = lo;

reverse(s.begin() + 1, s.end());
n = s.size() - 1;
l = 1, r = n;
while (s[l] == s[r] && l <= r) {
l++, r--;
}

if (l > r) {
cout << 0 << '\n';
return;
}

vector<int> h(27 + 1);
for (int i = l; i <= r; i++) {
h[s[i] - 'a']++;
}

vector<int> is2(n + 1), ned2(n + 1);
for (int i = 1; i <= n / 2; i++) {
if (s[i] != s[n - i + 1]) {
is2[i] = 1;
}
}

for (int i = n / 2 - 1; i >= 1; i--) {
if (is2[i + 1] == 1 || ned2[i + 1]) {
ned2[i] = 1;
}
}

int ll = 2, rr = r - l;
while (ll <= rr) {
int mid = (ll + rr) >> 1;
if (ck(mid)) {
rr = mid - 1;
} else {
ll = mid + 1;
}
}

ans = min(ans, ll);
cout << ans << '\n';
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);

int t;
cin >> t;

while(t--) {
solve();
}

return 0;
}