Codeforces Round #789 (Div. 2)
[Link](Dashboard - Codeforces Round #789 (Div. 2) - Codeforces)
A. Tokitsukaze and All Zero Sequence
题意
给你一个数组,每次你可将 a i = a j a_i=a_j ai=aj的一个变成 0 0 0或者任选两个使得他们等于 m i n ( a i , a j ) min(a_i,a_j) min(ai,aj),问你让数组变成全零的最少操作数
思路
- 贪心
贪心来想如果有 0 0 0,那就拿 0 0 0每次来做操作,如果有相等的就做操作一再做操作二,否则就做操作二搞出相同的,再做操作一搞出零即可。
Code
#include <bits/stdc++.h>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 1e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
int a[N];
int main() {
ios::sync_with_stdio(false), cin.tie(0);
int T;
cin >> T;
while (T -- ) {
cin >> n;
for (int i = 1; i <= n; i ++) cin >> a[i];
sort(a + 1, a + 1 + n);
int cnt = 0;
for (int i = 1; i <= n; i ++)
if (!a[i]) {
cnt ++;
}
else {
break;
}
bool ok = false;
for (int i = 2; i <= n; i ++)
if (a[i] == a[i - 1]) {
ok = true;
break;
}
if (cnt) cout << n - cnt << '\n';
else if (ok) cout << n << '\n';
else cout << n + 1 << '\n';
}
return 0;
}
B2. Tokitsukaze and Good 01-String (hard version)
题意
给你一个 0 / 1 0/1 0/1串,规定当且仅当所有连续字符相同的最长子串长度均为偶数时称为好串,一次操作可以任意改变一个位置的字符,问你让其改成好串最少需要多少次操作,在此基础上问你最少构成多少个最长子串。
思路
- 贪心
由于最终要划分成一系列的偶数串,所以每一个奇偶交替的位置如果例如 1 , 2 1,2 1,2对应的字符不同的话,一定要改变一次,可以改成 00 / 11 00/11 00/11。因此最少操作次数就是多少个奇偶位置字符不同。
如何构造最少的子串呢,因为奇偶不同的位置可以随便变成 00 / 11 00/11 00/11,因为我们将字符串按照这些改变的位置切成一段一段,对于每一段如果和前面一段字符一样那么他们就可以拼在一起。
Code
#include <bits/stdc++.h>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 1e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
int main() {
ios::sync_with_stdio(false), cin.tie(0);
int T;
cin >> T;
while (T -- ) {
cin >> n;
string str; cin >> str;
vector<char> ve;
int res = 0, cnt = 1;
for (int i = 0; i < n; i += 2)
if (str[i] != str[i + 1]) res ++;
else ve.push_back(str[i]), ve.push_back(str[i + 1]);
for (int i = 0; i + 1 < ve.size(); i ++)
if (ve[i] != ve[i + 1])
cnt ++;
cout << res << ' ' << cnt << '\n';
}
return 0;
}
C Tokitsukaze and Strange Inequality
题意
给你一排列 p p p,问你有多少组不同的数 [ a , b , c , d ] [a,b,c,d] [a,b,c,d]满足:
- 1 ≤ a < b < c < d ≤ n 1\le a<b<c<d\le n 1≤a<b<c<d≤n
- p a < p c , p b > p d p_a <p_c,p_b>p_d pa<pc,pb>pd
思路
- 枚举
暴力的话需要 O ( n 4 ) O(n^4) O(n4),肯定不行。考虑优化,想法是对于固定的某个数是否能直接找到某个区间内和他构成的数有多少,例如枚举 a , c a,c a,c,再枚举 b b b,这个时候对于每一个 b b b,我们相当于要找 c c c后面有多少个小于 p b p_b pb的数,这个可以前缀和预处理搞一下,这样复杂度是 O ( n 3 ) O(n^3) O(n3),还是有点高,考虑我们对于 b b b可以直接找到和他有限制关系的 d d d的数量,因此我们也可以通过 c c c直接找到和他有限制关系的 a a a的数量。
因此枚举 b , c b,c b,c然后对于每一个 b , c b,c b,c查一下 b b b之前有多少个小于 p c p_c pc的和 c c c之后有多少个小于 p b p_b pb的,相乘一下即可。
Code
#include <bits/stdc++.h>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 1e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
int a[N];
int mn[5010][5010], mx[5010][5010];
int main() {
ios::sync_with_stdio(false), cin.tie(0);
int T;
cin >> T;
while (T -- ) {
cin >> n;
for (int i = 1; i <= n; i ++) cin >> a[i];
for (int i = 1; i <= n; i ++)
for (int j = 1; j < i; j ++)
if (a[j] < a[i])
mn[i][j] = 1;
else mn[i][j] = 0;
for (int i = n; i; i --)
for (int j = n; j > i; j --)
if (a[j] < a[i])
mx[i][j] = 1;
else mx[i][j] = 0;
for (int i = 1; i <= n; i ++)
for (int j = 1; j < i; j ++)
mn[i][j] += mn[i][j - 1];
for (int i = 1; i <= n; i ++)
for (int j = n - 1; j > i; j --)
mx[i][j] += mx[i][j + 1];
LL res = 0;
for (int i = 2; i <= n; i ++)
for (int j = i + 1; j < n; j ++)
res += (LL)mn[j][i - 1] * mx[i][j + 1];
cout << res << '\n';
}
return 0;
}
D. Tokitsukaze and Meeting
题意
给你一个长为 n × m 的 0 / 1 n\times m 的0/1 n×m的0/1串和一个 n × m n\times m n×m的矩阵,从前往后执行 n × m n\times m n×m次操作,第 i i i次操作将第 i i i个字符放到矩阵的 ( 1 , 1 ) (1,1) (1,1)然后原来矩阵内的字符按照顺序后延,每次操作后输出有多少个行列包含至少一个 1 1 1。
思路
- 模拟, d p dp dp思想转移
首先行列是互不影响的所以我们分别考虑行列,假设当前操作了 i i i次
- 对于行:当前矩形应该是从第一行往下每一行依次是 [ i , i − m + 1 ] , [ i − m , i − 2 × m + 1 ] , . . . [i,i-m+1],[i-m,i-2\times m+1],... [i,i−m+1],[i−m,i−2×m+1],...我们无法复原这个矩阵,这样复杂度过高了,观察这个分布,我们实际上要看的是 [ i , i − m + 1 ] [i,i-m+1] [i,i−m+1]这个区间有没有 1 1 1决定了这一行是否被选,其他的所有行等价成放入第 i − m i-m i−m个字符时候有多少行成立的情况,直接由这个前驱转移过来即可。
- 对于列:拿个笔画一画发现,对于 i % m i\% m i%m相同的两个数一定在同一列,并且每一次加入一个数,一定是一整列循环的往后移动,因此我们只需要判断当前加入的 i % m i\%m i%m这一列是否有 1 1 1,如果没有 1 1 1并且当前字符为 1 1 1,我们就对这一列打个标记然后列的答案加一下即可。
Code
#include <bits/stdc++.h>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 1e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
int a[N];
int main() {
ios::sync_with_stdio(false), cin.tie(0);
int T;
cin >> T;
while (T -- ) {
cin >> n >> m;
vector<int> s(n * m + 1), col(m), row(n * m + 1);
int cnt = 0;
string str; cin >> str;
for (int i = 0; i < str.size(); i ++)
s[i + 1] = s[i] + (str[i] == '1');
for (int i = 1; i <= n * m; i ++) {
row[i] = row[max(0, i - m)] + (s[i] - s[max(0, i - m)] ? 1 : 0);
if (!col[i % m] && str[i - 1] == '1') col[i % m] = 1, cnt ++;
cout << row[i] + cnt << " \n"[i == n * m];
}
}
return 0;
}
E. Tokitsukaze and Two Colorful Tapes
题意
给你两个长度为 n n n的数组 a , b a,b a,b你需要在 a a a里填入 [ 1 , n ] [1,n] [1,n]的排列,且 b b b中和 a a a颜色相同的块具有一样的数字,先让你求 ∑ i = 1 n ∣ a i − b i ∣ \sum_{i=1}^n \mid a_i-b_i\mid ∑i=1n∣ai−bi∣的最大值。
思路
- 贪心,找环
首先我们 a i → b i a_i\to b_i ai→bi这个颜色连接一条边,则会形成若干个环,我们将边权定义为相邻两结点的权值差的绝对值,则所有边权和就等价于我们要求的东西,对于奇偶环我们分开来看。
- 偶数:对于环上的一个三个相邻的点 a , b , c a,b,c a,b,c来说,对应的边权为 ∣ b − a ∣ \mid b-a \mid ∣b−a∣, ∣ c − b ∣ \mid c - b \mid ∣c−b∣,假设 b b b的点权是其中最大的则为 b − a , b − c b-a,b-c b−a,b−c,假设偶数环有四个点 a < b < c < d a<b<c<d a<b<c<d,则边权为 2 × ( c + d ) − 2 × ( a + b ) 2\times (c+d)-2\times(a+b) 2×(c+d)−2×(a+b),为了让权值更大因此我们尽量让 c , d c,d c,d更大, a , b a,b a,b更小。
- 奇数:奇数同偶数不同的就是,会有一个点没有贡献,例如 1 , 2 , 3 1,2,3 1,2,3三个点,边权有 2 − 1 , 3 − 2 , 3 − 1 2-1,3-2,3-1 2−1,3−2,3−1,则点 2 2 2会消除掉,因此奇数环我们只能填 n − 1 n-1 n−1个有用的数,有一个会自己抵消,
那么就是我们有一些环,每个环有一半的点是正的 × 2 \times 2 ×2,有一半的点是负的 × 2 \times 2 ×2,贪心来看我们一定是,大的数赋予正权值,小的赋予负权值,中间的赋予零就是自己将自己抵消了,因此算一下有多少个没被抵消的点,即可。
Code
#include <bits/stdc++.h>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 1e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
int dx[] = {-1, 0, 1, 0}, dy[] = {0, 1, 0, -1};
int h[N], e[M], ne[M], w[M], idx;
void add(int a, int b, int v = 0) {
e[idx] = b, w[idx] = v, ne[idx] = h[a], h[a] = idx ++;
}
int n, m, k;
int a[N], b[N];
int f[N], cnt[N];
int find(int x) {
return x == f[x] ? x : f[x] = find(f[x]);
}
int main() {
ios::sync_with_stdio(false), cin.tie(0);
int T;
cin >> T;
while (T -- ) {
cin >> n;
for (int i = 1; i <= n; i ++)
cin >> a[i], cnt[i] = 1, f[i] = i;
for (int i = 1; i <= n; i ++) {
int x; cin >> x;
int xx = find(x), yy = find(a[i]);
if (xx != yy) {
f[xx] = yy;
cnt[yy] += cnt[xx];
}
}
int v = 0;
for (int i = 1; i <= n; i ++)
if (i == f[i])
v += cnt[i] / 2;
cout << 2ll * (n + n - v + 1) * v / 2 - 2ll * (1 + v) * v / 2 << '\n';
}
return 0;
}