Solution - Codeforces Round 1003 (Div. 4)

Codeforces Round 1003 (Div. 4)

A Skibidus and Amog’u

题意

把单数变复数

思路

Replace all "us"s with "s"s, and print them.

把所有结尾的"us"替换为"i"即可。

代码

1
2
3
4
5
6
7
8
9
10
11
12
#include<bits/stdc++.h>
using namespace std;
const int N = 101;
signed main(){
int T; cin >> T;
while (T--){
string x; cin >> x;
string y = x.substr(0, x.length() - 2) + "i";
cout << y << endl;
}
return 0;
}

B Skibidus and Ohio

题意

给定一个小写字母字串,每次可以选两个相邻相同的字母,消去并以任意字母替代。求最后剩余最短字串长度。

思路

观察到每次消去都可以选一个和相邻字母相同的字母,所以只要有两个相邻字母相同,答案就是11,否则答案是lengthlength

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include<bits/stdc++.h>
using namespace std;
signed main(){
int T; cin >> T;
while (T--){
string x; cin >> x;
bool flag = false;
for (int i = 0; i < x.length() - 1; ++i){
if (x[i] == x[i + 1]){
cout << 1 << endl;
flag = true;
break;
}
}
if (!flag) cout << x.length() << endl;
}
return 0;
}

C1 & C2 Skibidus and Fanum Tax

题意

给定aabb两个数列,每个aia_i可以不变,也可以变成bjaib_j-a_i(只能变一次,jj任取)。问能否保证aa单调不降。

思路

11nn遍历所有aia_i,我们当然希望每个aia_i在进行操作后尽可能小。每个aia_i有两种操作方式:

  1. 不变,需满足条件aiai1a_i \geq a_{i - 1}
  2. 找到一个jj,把aia_i变成bjaib_j - a_i,需要满足条件bjaiai1b_j - a_i \geq a_{i - 1}

其中22操作bjb_j越小越好,也就是说找到最小的bjb_j使bjaiai1b_j - a_i \geq a_{i - 1},即最小的满足bjai+ai1b_j\geq a_i + a_{i - 1}bjb_j。这个可以通过把所有bjb_j丢到一个set里面,然后用lower_bound查找即可。

复杂度O(nlogm+m)O(n\log m + m)

代码

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
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 1;
int n, m, A[N];
std::set<int> st;
signed main(){
int T; scanf("%d", &T);
A[0] = -1e9 - 1;
while (T--){
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; ++i){
scanf("%d", &A[i]);
}
for (int i = 1; i <= m; ++i){
int x; scanf("%d", &x);
st.insert(x);
}
bool fflag = true;
for (int i = 1; i <= n; ++i){
bool flag = false;
if (A[i] >= A[i - 1]){
flag = true;
}
auto it = st.lower_bound(A[i] + A[i - 1]);
int bb = (flag ? A[i] : 1e9 + 1);
if (it != st.end()){
flag = true;
A[i] = min(bb, (*it) - A[i]);
// printf("\nT = %d, i = %d, Found = %d, A[i] = %d\n", T, i, (*it), A[i]);
}
if (!flag){
puts("NO");
fflag = false;
break;
}
}
if (fflag){
puts("YES");
}
st.clear();
}
}

D Skibidus and Sigma

题意

给定一些长度相同的数列,请规定一种连接顺序,使最后的i=1nmj=1ibj\sum_{i = 1}^{nm}\sum_{j = 1}^i b_j最大。

思路

假设先按a1,a2,,ana_1, a_2, \dots, a_n排好。

假设i<ji < j,注意到aia_iaja_j如果交换的话,Δans=sum(aj)sum(ai)\Delta ans = sum(a_j) - sum(a_i),所以当任意i<ji < j都满足sum(ai)sum(ai)sum(a_i) \geq sum(a_i)的话就是最优的。按照sum(ai)sum(a_i)排序即可。

代码

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
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 1;
vector<int> A[N];
int idx[N], n, m;
LL sum[N];
inline bool cmp(const int& x, const int& y){
return sum[x] > sum[y];
}
signed main(){
int T; scanf("%d", &T);
while (T--){
scanf("%d%d", &n, &m);
memset(sum, 0, sizeof(sum));
for (int i = 1; i <= n; ++i){
for (int j = 0; j < m; ++j){
int x; scanf("%d", &x);
A[i].push_back(x);
sum[i] += A[i][j];
}
idx[i] = i;
}
sort(idx + 1, idx + 1 + n, cmp);
LL ans = 0;
for (int i = 1; i <= n ; ++i){
for (int j = 0; j < m; ++j){
ans += (LL)A[idx[i]][j] * (n * m - (i - 1) * m - j);
}
A[idx[i]].clear();
}
printf("%lld\n", ans);
}
}

E Skibidus and Rizz

题意

给定nn00mm11,询问是否能够用这n+mn+m个数构造一个01序列,使其所有子串01数量差的最大值恰好为kk,并构造解。

思路

我们只考虑0011多或相等的情况:

  1. nm>kn - m > k,则不管怎么摆这个串本身的01数量差大于kk,不可能;
  2. n<kn < k,则不管怎么摆都小于kk,不可能;
  3. 如果不是以上两种情况,我们先摆kk00,然后在后面跟上尽可能多的1010交替序列,最后再把多余的11摆上。由于最后剩11的个数为m(nk)m(mk)=km-(n-k) \leq m - (m-k) = k,所以最大值刚好是kk

时间复杂度O(n+m)O(n + m)

代码

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
#include<bits/stdc++.h>
using namespace std;
string construct(int n, int m, int k, bool flag){
if (n - m > k || k > n){
return "-1";
}
string s;
for (int i = 1; i <= k; ++i){
s += '0' + flag;
}
for (int i = 1; i <= n - k; ++i){
s += '0' + !flag;
s += '0' + flag;
}
for (int i = 1; i <= m - n + k; ++ i){
s += '0' + !flag;
}
return s;
}
signed main(){
ios::sync_with_stdio(false); cin.tie(0), cout.tie(0);
int T; cin >> T;
while (T--){
int n, m, k;
cin >> n >> m >> k;
bool flag = false;
if (n < m) flag = true, n ^= m ^= n ^= m;
cout << construct(n, m, k, flag) << endl;
}
}

F Skibidus and Slay

题意

给定一棵树,树上每个点有一个颜色,对于每个颜色,求是否存在一条起码有两个节点的简单路径,使得这个路径上有超过一半的节点颜色是这个颜色。

思路

  1. 如果一条合法路径上任意相邻节点都不同时是当前颜色,则当且仅当这个路径有奇数个节点且刚好有一半多一个节点都是这个颜色(交替)那么我们可以找到一个长度为33的子路径来替代这条路径;
  2. 如果一条合法路径上存在相邻节点同时是当前颜色,则可以用这两个节点替代这条路径。

综上,检测长度为2233的路径即可。

  1. 长度为22的路径数量就是边数,O(m)O(m)解决。
  2. 长度为33的路径,可以每个节点枚举相邻节点,看看有没有相同颜色,同样O(m)O(m)解决。

复杂度O(n+m)O(n + m)

代码

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
#include<bits/stdc++.h>
using namespace std;
const int N = 5e5 + 1;
// 🤔原来还可以打表情😂
int A[N], n;
bool vis[N], ans[N], flag[N];
vector<int> G[N];
void Dfs(int u){
if (vis[u]) return;
vis[u] = 1;
for (int i = 0; i < G[u].size(); ++i){
int v = G[u][i];
if (A[u] == A[v]) ans[A[u]] = 1;
if (flag[A[v]]) ans[A[v]] = 1;
else flag[A[v]] = 1;
}
for (int i = 0; i < G[u].size(); ++i){
int v = G[u][i];
flag[A[v]] = 0;
}
for (int i = 0; i < G[u].size(); ++i){
int v = G[u][i];
Dfs(v);
}
}
signed main(){
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int t; cin >> t;
while (t--){
cin >> n;
for (int i = 1; i <= n; ++i){
cin >> A[i];
G[i].clear();
ans[i] = 0, vis[i] = 0;
}
for (int i = 1; i < n; ++i){
int a, b; cin >> a >> b;
G[a].push_back(b);
G[b].push_back(a);
}
Dfs(1);
for (int i = 1; i <= n; ++i) cout << ans[i];
cout << endl;
}
return 0;
}

G Skibidus and Capping

题意

给定数列,求数列中的所有数对中,满足两者的lcm\operatorname{lcm}可以表示为两个质数乘积的(半质数)个数。

思路

先记录每个数的质因子个数,逐次考虑每个数xx

  1. xx是质数,则找到之前所有的质数(不等于xx),以及之前的包含xx的半质数;
  2. xx是半质数,则找到之前的xx的两个质数,或者xx

所有都可以O(n)O(n)维护,而判断是否为质数和分解质因数需要O(n)O(\sqrt n),复杂度O(nn)O(n\sqrt n)。也可以通过欧拉筛预处理达到O(n)O(n)的复杂度。

代码

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
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int N = 2e5 + 1;
int n, cnt1[N], cnt2[N], prnum;
LL ans;
inline bool isprime(int x){
for (int i = 2; i * i <= x; ++i){
if (x % i == 0){
return false;
}
}
return true;
}
inline PII divide(int x){
PII y = {0, 0};
for (int i = 2; i * i <= x;){
if (x % i == 0){
x /= i;
if (y.first) return PII(0, 0);
else y.first = i;
}else ++i;
}
if (x != 1) y.second = x;
return y;
}
signed main(){
int t; cin >> t;
while (t--){
cin >> n;
for (int i = 1; i <= n; ++i) cnt1[i] = cnt2[i] = 0;
prnum = 0, ans = 0;
for (int i = 1; i <= n; ++i){
// printf("i = %d\n",i);
int x; cin >> x;
if (isprime(x)){
cnt1[x] += 1, prnum += 1;
ans += prnum - cnt1[x];
ans += cnt2[x];
// printf("anssss += %d\n", prnum - cnt1[x] + cnt2[x]);
}else{
PII y = divide(x);
if (y.second == 0){
continue;
}
cnt2[y.first] += 1;
if (y.second != y.first) cnt2[y.second] += 1;
cnt1[x] += 1;
ans += cnt1[y.first];
// printf("first = %d, second = %d\n",y.first, y.second);
if (y.second != y.first) ans += cnt1[y.second];
ans += cnt1[x];
// printf("ans += %d %d %d\n", cnt1[y.first], cnt1[y.second], cnt1[x]);
}
}
printf("%lld\n", ans);
}
return 0;
}

H Bro Thinks He’s Him

题意

给定一个01序列,现作qq次修改,每次把某一位数反转,求每次修改完该序列所有子序列的01交替次数之和。

思路

首先考虑对于总序列如何求解:

f(i)f(i)为为第ii位及以前子序列交替次数之和,g(i)g(i)为第ii位及以前结尾为00的子序列交替次数之和,h(i)h(i)为第ii位及以前结尾为11的子序列交替次数之和;p(i)p(i)为第ii位及以前结尾为00的子序列数量,q(i)q(i)为第ii位及以前结尾为11的子序列数量;r(i)r(i)为第ii位及以后开头为00的子序列数量,s(i)s(i)为第ii位及以后开头为11的子序列数量。

假设第ii位为00,则:

g(i)=2g(i1)+h(i1)+q(i1)h(i)=h(i1)    f(i)=2f(i1)+q(i1)\begin{aligned} g(i) &= 2g(i - 1) + h(i - 1) + q(i - 1)\\ h(i) &= h(i - 1)\\ \implies f(i) &= 2f(i - 1) + q(i - 1)\end{aligned}

故我们可以得到:

f(i)={2f(i1)+q(i1)if x=02f(i1)+p(i1)if x=1f(i) = \begin{cases} 2f(i - 1) + q(i - 1) & \text{if } x = 0\\ 2f(i - 1) + p(i - 1) & \text{if } x = 1 \end{cases}

这样可以算出原序列的f(n)f(n)

现在考虑将中间某个数从00变成11,则我们只需考虑包含这个数的子序列:

左边最后一个为00,右边第一个为00,则答案会加2p(i1)r(i+1)2p(i - 1)r(i + 1);左边最后一个为11,右边第一个为00,则答案会减少2q(i1)s(i+1)2q(i - 1)s(i + 1);只有左边或右边00答案会增加p(i1)p(i - 1)r(i+1)r(i + 1);只有左边或右边11答案会减少q(i1)q(i - 1)s(i+1)s(i + 1)。其余情况答案没有变化。

p,q,r,sp,q,r,s都可以用树状数组动态维护(单点修改,区间查询)。复杂度O(nlogn)O(n\log n)

代码

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
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 1, MOD = 998244353;
class BIT{ // 树状数组
private:
int n; vector<int> T;
public:
inline void add(int x, int v){
for (; x <= n; x += x & -x) T[x] = (T[x] + v) % MOD;
}
inline int query(int x){
int res = 0;
for (; x; x -= x & -x) res = (res + T[x]) % MOD;
return res;
}
inline int sum(int x, bool flag){
if (flag){
return query(x);
}else{
return (query(n) - query(x - 1) + MOD) % MOD;
}
}
BIT(int x){
n = x;
T = vector<int>(n + 1);
}
};

int n, q, f[N], pow2[N];

signed main(){
int t; cin >> t;
pow2[0] = 1;
for (int i = 1; i < N; ++i) pow2[i] = pow2[i - 1] * 2 % MOD;
while (t--){
string A; cin >> A;
n = A.length();
BIT g(n), h(n), r(n), s(n); // g, h对应p, q
for (int i = 0; i < A.length(); ++i){
if (A[i] == '0'){
g.add(i + 1, pow2[i]);
r.add(i + 1, pow2[n - i - 1]);
f[i + 1] = (f[i] * 2 % MOD + h.sum(i, 1) + 1) % MOD;
}else{
h.add(i + 1, pow2[i]);
s.add(i + 1, pow2[n - i - 1]);
f[i + 1] = (f[i] * 2 % MOD + g.sum(i, 1) + 1) % MOD;
}
// printf("i = %d, fi = %d\n",i,f[i + 1]);
}
int ans = f[n];
cin >> q;
for (int i = 1; i <= q; ++i){
int x; cin >> x;
if (A[x - 1] == '0'){
ans = (ans + (LL)g.sum(x - 1, 1) * r.sum(x + 1, 0) * 2 % MOD + MOD) % MOD;
ans = (ans - (LL)h.sum(x - 1, 1) * s.sum(x + 1, 0) * 2 % MOD + MOD) % MOD;
ans = ((LL)ans + g.sum(x - 1, 1) - h.sum(x - 1, 1) + r.sum(x + 1, 0) - s.sum(x + 1, 0) + MOD * 2) % MOD;
g.add(x, MOD - pow2[x - 1]), r.add(x, MOD - pow2[n - x]);
h.add(x, pow2[x - 1]), s.add(x, pow2[n - x]);
A[x - 1] = '1';
}else{
ans = (ans - (LL)g.sum(x - 1, 1) * r.sum(x + 1, 0) * 2 % MOD + MOD) % MOD;
ans = (ans + (LL)h.sum(x - 1, 1) * s.sum(x + 1, 0) * 2 % MOD + MOD) % MOD;
ans = ((LL)ans - g.sum(x - 1, 1) + h.sum(x - 1, 1) - r.sum(x + 1, 0) + s.sum(x + 1, 0) + MOD * 2) % MOD;
h.add(x, MOD - pow2[x - 1]), s.add(x, MOD - pow2[n - x]);
g.add(x, pow2[x - 1]), r.add(x, pow2[n - x]);
A[x - 1] = '0';
}
cout << ans << ' ';
}
cout << endl;
}
return 0;
}