文章目录
Codeforces Round #629 (Div. 3)
B. K-th Beautiful String
题意: 给定一个长度n,一个次序m,定义一个特殊字符串s为:s中只含有a和b,且b的个数为2。现在要求大于长度为n的,次序为m的字符串。
题解: 规律题。观察规律可以知道:
bb .... 1
bab .... 2
bba .... 3
baab .... 4
baba .... 5
bbaa .... 6
baaab .... 7
baaba .... 8
babaa .... 9
bbaaa .... 10
从上面可以看出,长度为2的有1个,长度为3的有2个,长度为4的有3个,那么第一个b的位置,从右往左数(从0开始计数)就是长度值,记为first,第二个b的位置就是first+1+m-sum[first],sum数组为每组个数的前缀和
代码:
#include <bits/stdc++.h>
#define int long long
using namespace std;
int const MAXN = 5e5 + 10;
int n, m, T, sum[MAXN];
char ans[MAXN];
signed main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
cin >> T;
for (int i = 1; i <= 5e5; ++i) sum[i] = sum[i - 1] + i;
while(T--) {
cin >> n >> m;
for (int i = 0; i < n; ++i) ans[i] = 'a';
for (int i = 1; i <= 5e5; ++i) {
if (m - sum[i] <= 0) {
ans[n - i - 1] = 'b';
ans[n - i + sum[i] - m] = 'b';
break;
}
}
for (int i = 0; i < n; ++i) cout << ans[i];
cout << endl;
}
return 0;
}
D. Carousel
题意: 给定n个数字,这n个数字首尾相连。现在要求给这些数字染色,要求相邻的不同数字必须染上不同的颜色,问最少需要多少颜色才能够将所有数字填满颜色。
题解: 最多的颜色只需要3种。
如果全部动物的类型都一样,那么1种颜色。
如果动物的类型>=2, 那么如果没有相邻的同色动物,且长度为偶数,那么1212填即可。
如果动物的类型>=2,那么如果有相邻的同色动物,且长度为奇数,我也填1212,然后找到一个相邻的动物的pos ~ n开始反转颜色。
如果动物的类型>=2,那么如果没有相邻的同色动物,且长度为奇数,那么前面也是1212,最后一个填为3.
代码:
#include <bits/stdc++.h>
using namespace std;
int attr[200050], res[200050];
int main() {
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
int T;
cin >> T;
while (T--) {
int n, i, p = 1, type = 1, flg = 0;
cin >> n >> attr[1];
res[1] = 1; //第一只动物直接处理成颜色1
for (i = 2; i <= n; i++) {
cin >> attr[i];
if (attr[i] != attr[i - 1]) {
type = 2; //出现不同种类动物,种类数先变为2
p = 3 - p;
res[i] = p;
}
else {
flg = i; //记录相邻种类相同的位置
res[i] = p;
}
}
if (n >= 3 && attr[1] != attr[n] && res[1] == res[n]) {
if (flg) {
for (i = flg; i <= n; i++)
res[i] = 3 - res[i]; //flg其后全部翻转即可
}
else {
type = 3;
res[n] = 3;
}
}
cout << type << '\n';
for (i = 1; i <= n; i++) cout << res[i] << ' ';
cout << '\n';
}
return 0;
}
E. Tree Queries
题意: 给定一颗有n个节点的树,然后有m个询问,每次询问给定一个长度为t的数组,判断是否存在一条路径,使得路径上的点要不然位于数组中,要不然距离数组中的某个点距离为1.
题解: dfs+思维。对于路径中的每个点,其父节点必然存在于路径中,因此我们可以把所有的点均转换为父节点。那么我们只需要判断这些父节点是否均位于路径中即可,如果均位于路径中,那么必然存在路径。那么现在如果判断所有点是否位于一条路径上,我们可以维护每个点深度,然后按照深度排序,判断深度大的点是否在深度小的点的子树中即可,判断是否位于子树只需要使用dfs序就能够判断。
代码:
#include <bits/stdc++.h>
#define int long long
using namespace std;
int const MAXN = 2e5 + 10, MAXM = MAXN * 2;
int n, m, T;
int e[MAXM], ne[MAXM], h[MAXN], idx, dfn[MAXN], tot, ed[MAXN];
int father[MAXN], dep[MAXN];
void add(int a, int b) {
e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
void dfs(int u, int fa) {
dfn[u] = ++tot; // dfn[i]=j表示i的dfs序为j
father[u] = fa;
for (int i = h[u]; ~i; i = ne[i]) {
int j = e[i];
if (j == fa) continue;
dep[j] = dep[u] + 1;
dfs(j, u);
}
ed[u] = tot; // ed[i]=j表示i的子树的最后一个节点的dfs序为j
}
signed main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
cin >> n >> m;
for (int i = 1; i <= n; ++i) h[i] = -1;
for (int i = 1; i <= n - 1; ++i) {
int a, b;
cin >> a >> b;
add(a, b), add(b, a);
}
dfs(1, -1);
for (int i = 1, k; i <= m; ++i) {
cin >> k;
vector<pair<int, int> > path;
for (int j = 1, t; j <= k; ++j) {
cin >> t;
if (t != 1) t = father[t];
else t = 1;
path.push_back({dep[t], t});
}
sort(path.begin(), path.end());
reverse(path.begin(), path.end());
int flg = 1;
for (int i = 0; i < path.size() -1 ; ++i) {
if (dfn[path[i].second] < dfn[path[i + 1].second] || dfn[path[i].second] > ed[path[i + 1].second]) {
flg = 0;
break;
}
}
if (flg) cout << "YES\n";
else cout << "NO\n";
}
return 0;
}
F. Make k Equal
题意: 给一个 n 和 k,以及长度为 n 的序列 a。
有两种操作:
- 让数组中其中一个最小值的值加一。
- 让数组中其中一个最大值的值减一。
问:最少几次操作,可以使得数组中至少有 k 个数相等。 1 < = k , n < = 2 ∗ 1 0 5 , 1 < = a i < = 1 0 9 1<=k,n<=2*10^5,1<=a_i<=10^9 1<=k,n<=2∗105,1<=ai<=109
题解: 必定存在最优解,满足最后相等的 k 个数的值,为 a 中出现过的值。那么我们就可以排序后,直接枚举最后出现的值。对于当前的值a[i],如果比它小的有k个,那么我们可以将比他小的值变为a[i],这样修改的次数就是A = i * a[i] - s[i] - (i - k);如果比它大的有k个,还可以把比他大的值变为a[i],这样修改次数就是B = t[i] - (n - i + 1) * a[i] - (n - i + 1 - k);如果比他小或者比他大的不足k个,那么我们既需要把比他小的,也需要把比它大的变为a[i],这样修改次数为A + B - k + 1。枚举到当前的a[i]时,直接枚举这三种情况,取个min即可。
代码:
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int MAXN = 200002;
int n, k, a[MAXN], s[MAXN], t[MAXN], c[MAXN], r = 1e18, A, B;
signed main() {
scanf("%lld%lld", &n, &k);
for (int i = 1; i <= n; ++i) scanf("%lld", a + i);
sort(a + 1, a + n + 1);
for (int i = 1; i <= n; ++i) s[i] = s[i - 1] + a[i];
for (int i = n; i >= 1; --i) t[i] = t[i + 1] + a[i];
for (int i = 1; i <= n; ++i) {
if (a[i] == a[i - 1])
c[i] = c[i - 1] + 1;
else
c[i] = 1;
if (c[i] >= k) {
puts("0");
return 0;
}
}
for (int i = 1; i <= n; ++i) {
A = i * a[i] - s[i] - (i - k);
B = t[i] - (n - i + 1) * a[i] - (n - i + 1 - k);
if (i >= k) r = min(r, A);
if (n - i + 1 >= k) r = min(r, B);
r = min(r, A + B - k + 1);
}
printf("%llu\n", r);
return 0;
}