Codeforces Round #629 (Div. 3)
A Divisibility Problem
题意
给你两个正整数\(a,b\) 你每次可以执行一次\(a++\)操作,问你最小的操作步数,使得\(a\)能被\(b\)整除
思路
数学
分情况讨论
当\(a<b\) 时,显然只有\(a == b\)时才能保证\(a\)被\(b\)整除
当\(a>b\) 时,只要把\(a\)调整到离\(kb\)最近的一个\(b\)的倍数,即\(\lceil {a / b}\rceil * b - a\)
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define IO ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
typedef long long LL;
typedef double db;
typedef pair<int,int> PII;
const int N = 1e5 + 10;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const db EXP = 1e-9;
int main() {
IO;
int t,a,b;
cin >> t;
while(t --) {
int cnt = 0;
cin >> a >> b;
if(a < b) cnt += (b - a);
else cnt += ((a + b - 1)/ b) * b - a;
cout << cnt << endl;
}
return 0;
}
B K-th Beautiful String
题意
给定一个\(n>2\)和\(k\),初始化字符串的前\(n-2\)个字符为\(a\),最后两个字符为\(b\),此为字符串的第一个排列,所有字符串的按字典序排列,让你求第\(k\)个字符串
思路
二进制,双指针,贪心
把\(a\)看成\(0\),\(b\)看成\(1\),整个字符串的排列就是一个二进制加法
每次,排列的名次\(+1\),整个字符串就加上它的\(lowbit()\),为了保证整个字符串只有两个\(b\)
如果lowbit发生了进位,就要在末尾再加上一个\(1\)
具体做法不能直接按二进制加法来,因为题目的数据范围是\(1e5\),\(int,long long\)等数据类型都达不到这么长的数据位,所以考虑维护两个指针\(i,j\) 分别表示第一个\(b\)的位置和第二个\(b\)的位置
如果\(i+1==j\)说明\(i,j\)相邻,那么他们相加必然导致进位,所以\(i++,j=n\),
如果\(i,j\)不相邻,每次只需要处理\(j\)的进位即可,\(j++\)
循环\(k\)次,\(i,j\)的位置即是两个\(b\)的位
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define IO ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
typedef long long LL;
typedef double db;
typedef pair<int,int> PII;
const int N = 1e5 + 10;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const db EXP = 1e-9;
int main() {
IO;
int t,n,k;
cin >> t;
while(t --) {
cin >> n >> k;
int i = n - 1,j = n;// b is here;
k --;
while(k --) {
if(i + 1 == j) {
i --;
j = n;
}
else {
j --;
}
}
for(int k = 1;k <= n; ++k) {
if(k == i || k == j) cout << 'b';
else cout << 'a';
}
cout << endl;
}
return 0;
}
C Ternary XOR
题意
给定一个三进制的数\(c\),定义运算符\(⊙\) 表示\(c_i=(a_i+b_i)\mod3\)
让你求另外两个三进制数\(a,b\),使得\(c=a⊙b\),且\(max(a,b)\)最小
思路
贪心
因为它的运算不涉及进位,只对当前位有影响,所以只考虑当前位即可
对于\(c_i\)的每一位,先尽量保持均分到\(a_i,b_i\)
如果某一位在过程中发生了\(0,1\)分配,那么接下来的所有数字都分配到\(0\)那一位后面即可
这样就能使得\(a,b\)尽可能相近了。
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define IO ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
typedef long long LL;
typedef double db;
typedef pair<int,int> PII;
const int N = 5e4 + 10;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const db EXP = 1e-9;
int a[N],c[N],b[N];
int main() {
IO;
int t,n;
char x;
cin >> t;
while(t --) {
memset(b,0,sizeof b);
memset(c,0,sizeof c);
cin >> n;
for(int i = 0;i < n; ++i) {
cin >> x;
a[i] = x - '0';
}
b[0] = c[0] = 1;
int f = 0;
for(int i = 1;i < n; ++i) {
if(a[i] == 1 && !f) {
if(b[i - 1] && !c[i - 1]) c[i] = 1;
else if(!b[i - 1] && c[i - 1]) b[i] = 1;
else b[i] = 1,f = 1;
}
else if(a[i] == 2 && !f) {
if(b[i - 1] && !c[i - 1]) c[i] = 2;
else if(!b[i - 1] && c[i - 1]) b[i] = 2;
else {
b[i] = 1;
c[i] = 1;
}
}
else if(f) {
for(int j = i;j < n; ++j) {
c[j] = a[j];
}
break;
}
}
for(int i = 0;i < n; ++i) cout << b[i];
cout << endl;
for(int i = 0;i < n; ++i) cout << c[i];
cout << endl;
}
return 0;
}
D Carousel
题意
给你一个环形序列,序列中的每个数字有自己的类型\(type\),请你想出一种染色方法,
使用尽可能小的颜色,把这个序列全部染色且满足以下条件
- 相邻数字的\(type\)不相同的话,不能染成同一种颜色
思路
染色,贪心
分成三种情况讨论
1.数组内的所有元素都相等
- 这种情况下的全部染成同一种颜色即可
2.\(n\)为偶数
- 把所有数按照\(1,2,1,2,\dots,\)这样染色即可
3.\(n\)为奇数
- 找到相邻的一对数字,他们的\(type\)相同,可以把他们缩在一起,就可以转化为\(n\)为偶数的情况
- 把相邻且\(type\)相同的这两个数字染成同一种颜色,看成一个数字,染色,剩下的继续\(1,2,1,2\)染色即可
- 注意特判首尾相同的情况
4.最后一种情况
- 假如到了这里,说明这个数列的长度为奇数,且数列中没有相邻的两个数字\(type\)相同
所以我们可以把最后一个数字染成\(3\),这样就转化成了\(n\)为偶数的情况
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define IO ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
typedef long long LL;
typedef double db;
typedef pair<int,int> PII;
const int N = 2e5 + 10;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const db EXP = 1e-9;
int f[N];
int main() {
IO;
int t,n;
cin >> t;
while(t --) {
int flag = -1,cnt = 1;
cin >> n;
for(int i = 0;i < n; ++i) {
cin >> f[i];
if(i && f[i] == f[i - 1]) flag = i,cnt ++;
}
if(f[0] == f[n - 1]) flag = 0;
if(cnt == n) {
cout << 1 << endl;
for(int i = 0;i < n; ++i) cout << 1 <<' ';
}
else if(n % 2 == 0) {
cout << 2 << endl;
for(int i = 0;i < n; ++i) cout << (i % 2 ? 1 : 2) <<' ';
}
else if(n % 2 == 1 && flag != -1 && flag != 0) {
cout << 2 << endl;
for(int i = 0;i < flag ; ++i) cout << (i % 2 ? 1 : 2) << ' ';
for(int i = flag;i < n; ++i) cout << (i % 2 ? 2 : 1) <<' ';
}
else if(n % 2 == 1 && flag == 0) {
cout << 2 << endl;
for(int i = 0;i < n - 1; ++i) cout << (i % 2 ? 1 : 2) << ' ';
cout << 2;
}
else {
cout << 3 << endl;
for(int i = 0;i < n - 1; ++i) cout << (i % 2 ? 1 : 2) <<' ';
cout << 3 ;
}
cout << endl;
}
return 0;
}
E
题意
给定一颗包含\(n\)个节点的有根树,树根的坐标为\(1\)。有\(m\)次询问,每次都给你一条链
问是否存在一条路径使得链上的每一个点到达该路径的距离小于等于\(1\)
思路
LCA
找到链中最深的节点\(k\),与其他所有节点求\(LCA\),判断\(LCA\)与当前节点\(v\)的距离是否大于\(1\)
显然,节点\(k\)向上的祖先路径可以构成一条满足题目要求的路径,所以只用检查每个祖先与其他点的距离即可
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define IO ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define lowbit(x) ((x)&(-x))
typedef long long ll;
typedef double db;
typedef pair<int,int> PII;
const int N = 2e5 + 10,M = 5e5 + 10;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const db EXP = 1e-9;
int e[M],ne[M],h[N],idx,q[N];
int depth[N],fa[N][20];
void add(int a,int b) {
e[idx] = b;
ne[idx] = h[a];
h[a] = idx ++;
}
void bfs(int root) {
memset(depth,0x3f,sizeof depth);
depth[0] = 0,depth[root] = 1;
queue<int> q;
q.push(root);
while(q.size()) {
int t = q.front();
q.pop();
for(int i = h[t]; ~i;i = ne[i]) {
int j = e[i];
if(depth[j] > depth[t] + 1) {
depth[j] = depth[t] + 1;
q.push(j);
fa[j][0] = t;
for(int k = 1;k <= 19; ++k)
fa[j][k] = fa[fa[j][k - 1]][k - 1];
}
}
}
}
int lca(int a,int b) {
if(depth[a] < depth[b]) swap(a,b);
for(int k = 19;k >= 0; --k) {
if(depth[fa[a][k]] >= depth[b])
a = fa[a][k];
}
if(a == b) return a;
for(int k = 19;k >= 0; --k) {
if(fa[a][k] != fa[b][k]) {
a = fa[a][k];
b = fa[b][k];
}
}
return fa[a][0];
}
int main() {
IO;
int n,m,a,b;
memset(h,-1,sizeof h);
cin >> n >> m;
for(int i = 0;i < n - 1; ++i) {
cin >> a >> b;
add(a,b);
add(b,a);
}
bfs(1);
while(m --) {
int maxdp = 1,k,flag = 1;
cin >> k;
for(int i = 0;i < k; ++i) {
cin >> q[i];
if(depth[maxdp] < depth[q[i]]) maxdp = q[i];
}
for(int i = 0;i < k; ++i) {
int anc = lca(q[i],maxdp);
if(abs(depth[anc] - depth[q[i]]) > 1) {
flag = 0;
break;
}
}
if(flag) cout << "YES" << endl;
else cout << "NO" << endl;
}
return 0;
}
F
题意
给定一个数组,你每次只能使数组中的某个最小的元素\(+1\)或者最大的元素\(-1\),问你最小的操作次数,得到\(k\)个相等的元素
思路
贪心,思维
因为只能操作最大元素和最小元素,所以最终的结果肯定是
\(x-1,x-1,x-1,x,x,\dots,x,x+1,x+1\)
需要取\(k\)个相同的元素,那么取\(x\)为最终元素,左边的再取\(k-sum[x]\)就好,不需要全部取完
右边同理。
设\(x-1\)的个数为\(L\),\(x\)的个数为\(sum\),\(x+1\)的个数为\(R\)
分三种情况:
1.\(L >= k - sum\) ,全部取左边
2.\(R >= k - sum\) ,全部取右边
3.两边都取
三者取\(min\)即是答案
#include <bits/stdc++.h>
using namespace std;
#define endl '\n'
#define IO ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define lowbit(x) ((x)&(-x))
typedef long long ll;
typedef double db;
typedef pair<int,int> PII;
const int N = 2e5 + 10;
const int mod = 1e9 + 7;
const int INF = 0x3f3f3f3f;
const ll INFL = 1e18;
const db EXP = 1e-9;
ll a[N],pre[N],suf[N];
int main() {
IO;
ll n,k;
cin >> n >> k;
for(int i = 1;i <= n; ++i) cin >> a[i];
sort(a + 1,a + n + 1);
// 注意这里只能用map,自带离散化,不然就需要二分,或者手动离散化了
// int直接存小标会爆掉
map<int,int> l,r;
for(int i = 1;i <= n; ++i) {
pre[i] = pre[i - 1] + a[i];
if(l[a[i]] == 0) l[a[i]] = i;
}
for(int i = n;i >= 1; --i) {
suf[i] = suf[i + 1] + a[i];
if(r[a[i]] == 0) r[a[i]] = i;
}
ll mini = INFL,Lcost,Rcost;
for(int i = 1;i <= n; ++i) {
ll delta = k - (r[a[i]] - l[a[i]] + 1);
if(delta <= 0) {
mini = 0;
break;
}
Lcost = (l[a[i]] - 1) * (a[i] - 1) - pre[l[a[i]] - 1];// 计算左边到达x - 1需要的操作步数
Rcost = suf[r[a[i]] + 1] - (n - r[a[i]]) * (a[i] + 1);// 计算右边到达x + 1需要的操作步数
if(i >= k) mini = min(mini,Lcost + delta);// 如果左边的个数大于 k ,只提取 delta 个元素 + 1,其余的不操作
if(i <= n - k + 1) mini = min(mini,Rcost + delta);// 如果右边的个数大于 k , 只提取 delta 个元素 - 1,其余的步操作
mini = min(mini,Lcost + Rcost + delta);// 两边都取元素 ,两边合起来,只需要delta个元素进行 + 1 或者 -1 操作
}
cout << mini << endl;
return 0;
}