Codeforces Round #789 (Div. 2) B~F
B.Tokitsukaze and Good 01-String
链接
题意:给出01字符串,要求每次可以修改一个地方,最后要让连续的1和连续的0的长度都是偶数,输出最下操作次数和最后的段数
思路:首先考虑都变成奇数,从头开始每次跳2,假如不一样,说明出现了奇数的0或者1,就需要变成,然后统计段数,当和最后一次记录不同的时候,说明段数加一
#include<bits/stdc++.h>
using namespace std;
int main()
{
int T;
cin >> T;
while (T--) {
int n;string s;
cin >> n >> s;
int ans1 = 0, ans2 = 0;
char last = '_';
for (int i = 0; i < n; i += 2) {
if (s[i] != s[i + 1]) {
ans1++;
}else {
if (last != s[i]) ans2++;
last = s[i];
}
}cout << ans1 << ' ' << max(1, ans2) << endl;
}
return 0;
}
Tokitsukaze and Strange Inequality
链接
题意:给出数组p,数组元素是
1
n
1~n
1 n的排列,要找出4个下标
a
,
b
,
c
,
d
a,b,c,d
a,b,c,d,要满足
a
<
b
<
c
<
d
&
&
p
a
<
p
c
a<b<c<d\&\&p_a<p_c
a<b<c<d&&pa<pcand
p
b
>
p
d
p_b>p_d
pb>pd,问可以找多少组。
思路:利用一个前缀和的思想直接处理出来每个数前面比他大的然后枚举b,c就完了
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 5050;
int cnt[N][N], a[N];
signed main()
{
int T;
cin >> T;
while (T--) {
int n;
cin >> n;
for (int i = 1; i <= n; i++) {cin >> a[i];}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
cnt[i][j] = cnt[i - 1][j];
}
for (int j = a[i]; j <= n; j++) {
cnt[i][j]++;
}
}
int ans = 0;
for (int i = 1; i <= n; i++) {
for (int j = i + 1; j <= n; j++) {
ans += (cnt[i - 1][a[j] - 1] * (cnt[n][a[i]] - cnt[j][a[i]]));
}
}cout << ans << endl;
}
return 0;
}
D.Tokitsukaze and Meeting
链接
题意:给出一个01字符串,然后有一个
n
∗
m
n*m
n∗m的矩阵,每次顺序往里面加入字符串的一个元素,前面的依次后移,如果一排满了则到下一排的第一个,对于每一排或者每一列,如果有1那就是一个好的排或列,问每次加入新元素后有几个好的排和列
思路:首先对于已经出现过的状态,比如0100,这时候记录他排的贡献,那么在经过了m次加入后,这个状态会再次出现,但是多了新的一排,所以我们可以定义dp数组,有
d
p
[
i
]
=
d
p
[
i
−
m
]
+
(
c
n
t
!
=
0
)
dp[i] = dp[i-m]+(cnt!=0)
dp[i]=dp[i−m]+(cnt!=0),其中cnt是第一排1的个数,而对于列,实际上我们每次都可以看成吧最后一列移到第一列然后加入一个0或者1,所以每一列最多提供1的答案,我们只要记录这一列是不是第一次出现1就行,最后的答案就是
a
n
s
列
+
d
p
[
i
]
ans_列+dp[i]
ans列+dp[i]
#include<bits/stdc++.h>
using namespace std;
int dp[1000010], r[1000010];
int n, m;
string s;
int main()
{
int T;
cin >> T;
while (T--) {
cin >> n >> m;
cin >> s;
for (int i = 0; i <= max(n,m); i++) r[i] = 0, dp[i] = 0;
int cnt = 0;int ans = 0;
for (int i = 0; i < n * m; i++) {
r[i % m] += s[i] - '0';
if ((r[i % m] == 1) && (s[i] == '1')) ans ++;
cnt += s[i] - '0';
if (i >= m) {
cnt = cnt - (s[i - m] - '0');
}
if (i >= m) {dp[i] = dp[i - m] + (!(cnt == 0));}
else dp[i] = (!(cnt == 0));
cout << ans + dp[i] << ' ';
}cout << endl;
}
return 0;
}
E.Tokitsukaze and Two Colorful Tapes
链接
题意:给出数组ab,他们都是
1
n
1~n
1 n的排列,可以把ab里面相同的数字变成另一个数字,最后也要是
1
n
1~n
1 n的排列,然后需要求
m
a
x
(
∑
i
=
1
n
∣
a
i
−
b
i
∣
)
max(\sum_{i=1}^{n}|a_i-b_i|)
max(∑i=1n∣ai−bi∣),问max的值
思路:对于每个位置可以看成
a
i
a_i
ai到
b
i
b_i
bi连接一条边,最后会构成一个个环,我们要让最大,也就是在环内部我们要大小大小的交替出现,求和公式为
∣
c
1
−
c
2
∣
+
∣
c
2
−
c
3
∣
.
.
.
.
∣
c
n
−
c
1
∣
|c_1-c_2|+|c_2-c_3|....|c_n-c_1|
∣c1−c2∣+∣c2−c3∣....∣cn−c1∣,最后就是,一半大数一半小数,交替出现,大数一定是累加上答案,小数就是累减上答案,并查集或者dfs统计环大小。
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e5+10;
int fa[N], siz[N], a[N], b[N];
int find(int n) {return n == fa[n] ? n : fa[n] = find(fa[n]);}
signed main()
{
int T;
cin >> T;
while (T--) {
int n;
cin >> n;
for (int i = 1; i <= n; i++) fa[i] = i, siz[i] = 1;
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 1; i <= n; i++) cin >> b[i];
for (int i = 1; i <= n; i++) {
int x = a[i], y = b[i];
x = find(x), y = find(y);
if (x == y) continue;
fa[x] = y;
siz[y] += siz[x];
}int idx = 0;
for (int i = 1; i <= n; i++) {
if (fa[i] == i) {
idx += siz[i] / 2;
}
}
int ans = 0;
for (int i = 1; i <= idx; i++) {
ans -= i;
}
for (int i = n - idx + 1; i <= n; i++) {
ans += i;
}
cout << ans * 2 << endl;
}
return 0;
}