A
每次操作可以选择任意个元素同时减1,问最少多少次操作能让所有元素相等
- 显然是最大值和最小值的差值
#include <bits/stdc++.h>
using namespace std;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while(t--){
int n;
cin >> n;
vector<int> a(n);
for(auto &i : a) cin >> i;
cout << *max_element(a.begin(), a.end()) - *min_element(a.begin(), a.end()) << '\n';
}
return 0;
}
B
给你三个数,不改变顺序,用一个正数m乘其中一个数,问能不能构成一个等差数列
- 利用等差数列性质,分情况讨论即可
#include <bits/stdc++.h>
using namespace std;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while(t--){
int a, b, c;
cin >> a >> b >> c;
if((a + c) % (2 * b) == 0 && (a + c) / (2 * b) > 0) cout << "YES" << '\n';
else if((2 * b - c) % a == 0 && (2 * b - c) / a > 0) cout << "YES" << '\n';
else if((2 * b - a) % c == 0 && (2 * b - a) / c > 0) cout << "YES" << '\n';
else cout << "NO" << '\n';
}
return 0;
}
C
给你若干个数,每次可以对其中一个数除以2向下取整,问进行若干次操作之后能不能使得这些数构成一个[1,n][1,n][1,n]的全排列
- 如果一个数已经处于全排列范围里,且没有重复,我们不需要改变,因为如果一个更大的数能够到达已经出现在数组里面的数,那么他也能够到达这个数能够到达的更小的数,所以只需要判断一下即可
#include <bits/stdc++.h>
using namespace std;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while(t--){
int n;
cin >> n;
set<int> st;
for(int i=1;i<=n;i++){
st.insert(i);
}
for(int i=0;i<n;i++){
int x;
cin >> x;
if(st.find(x) != st.end()){
st.erase(x);
}else{
while(x > 0 && st.find(x) == st.end()){
x /= 2;
}
if(st.find(x) != st.end()){
st.erase(x);
}
}
}
if(st.size() == 0) cout << "YES\n";
else cout << "NO\n";
}
return 0;
}
D
用kkk种颜色涂色,要求每一种颜色涂完之后构成的字符串是回文,该种颜色字符之间可以互换位置,尽可能使每一种颜色的字符串尽可能的长,问这其中最短的字符串的长度
- 对于出现偶数次的字符,我们可以两个两个的涂色,这样一定是回文,涂完偶数,开始涂奇数,先把奇数里面的偶数部分涂完,注意偶数一定要把所有颜色都用到,否则偶数分成两个奇数来处理,然后一个一个的放奇数,因为奇数只能每组放一个,所以放完即可
#include <bits/stdc++.h>
using namespace std;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while(t--){
int n, k;
cin >> n >> k;
string s;
cin >> s;
int len = s.length();
map<char, int> mp;
for(int i=0;i<len;i++){
mp[s[i]] += 1;
}
int even = 0;
int odd = 0;
int ans = 0;
for(auto i : mp){
even += i.second / 2;
if(i.second & 1) odd += 1;
}
ans += 2 * (even / k);
if(2 * (even % k) + odd >= k) ans += 1;
cout << ans << '\n';
}
return 0;
}
E
给出若干个电话号,现在问新給一个电话号能不能由这些已经给出的电话号中的若干个子字符串(长度至少是2)构成,如果能输出方案,否则输出−1-1−1
- 首先对输入的字符串进行预处理,因为任意一个大于等于2的数一定能够拆成若干个2和3组合的形式,所以用f2[i][j]f_2[i][j]f2[i][j]表示含有子串ijijij的字符串的相关信息(第几个字符串,从第几个位置开始),可以使用tuple类型存储这些信息;用f3[i][j][k]f_3[i][j][k]f3[i][j][k]表示含有子串ijkijkijk的字符串的相关信息
- 预处理好了以后,输入新的字符串,用dp[i]dp[i]dp[i]表示第i−1i-1i−1个字符是否匹配,因为匹配过程必然是连续的,所以使用一个简单的状态转移即可算出dp[m]dp[m]dp[m] ,如果dp[m]dp[m]dp[m]为truetruetrue,那么说明有解
- 类似01背包输出答案的方法,我们从m开始向前推,存储答案最后倒序输出即可
#include <bits/stdc++.h>
using namespace std;
using tp = tuple<int, int, int>;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while(t--){
int n, m;
cin >> n >> m;
tp f2[10][10], f3[10][10][10];
for(int i=0;i<n;i++){
string s;
cin >> s;
for(int j=0;j<m;j++){
if(j + 1 < m) f2[s[j] - '0'][s[j + 1] - '0'] = {j + 1, j + 2, i + 1};
if(j + 2 < m) f3[s[j] - '0'][s[j + 1] - '0'][s[j + 2] - '0'] = {j + 1, j + 3, i + 1};
}
}
string s;
cin >> s;
vector<bool> dp(m + 1);
dp[0] = true;
for(int i=0;i<m;i++){
if(!dp[i]) continue;
if(i + 1 < m && f2[s[i] - '0'][s[i + 1] - '0'] != tp(0, 0, 0)) dp[i + 2] = true;
if(i + 2 < m && f3[s[i] - '0'][s[i + 1] - '0'][s[i + 2] - '0'] != tp(0, 0, 0)) dp[i + 3] = true;
}
if(!dp[m]){
cout << -1 << '\n';
continue;
}else{
int i = m;
vector<tp> ans;
while(i > 0){
if(i >= 1 && dp[i - 2] && f2[s[i - 2] - '0'][s[i - 1] - '0'] != tp(0, 0, 0)){
ans.push_back(f2[s[i - 2] - '0'][s[i - 1] - '0']);
i -= 2;
}else{
ans.push_back(f3[s[i - 3] - '0'][s[i - 2] - '0'][s[i - 1] - '0']);
i -= 3;
}
}
reverse(ans.begin(), ans.end());
cout << ans.size() << '\n';
for(auto i : ans){
cout << get<0>(i) << ' ' << get<1>(i) << ' ' << get<2>(i) << '\n';
}
}
}
return 0;
}
F
交互题,给你一个初始值,范围是[1,n)[1,n)[1,n),每次可以加上一个ccc,c∈[1,n)c\in[1,n)c∈[1,n),每次询问同时返回⌊xn⌋\lfloor\frac{x}{n}\rfloor⌊nx⌋,要求在10次以内得到当前真实值
- 我们使用二分让最终答案逐步逼近,目标就是nnn的倍数,每次给返回值补上和距离最近的nnn的倍数相差的值,逐步逼近就能得到当前答案真实值
- 需要注意每次对左右边界都要加上补上的值,确保答案始终在这一区间内
#include <bits/stdc++.h>
using namespace std;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n;
cin >> n;
int l = 0;
int r = n + 1;
while(r - l > 1){
int mid = r + l >> 1;
int q = mid / n * n + n - mid;
l += q;
r += q;
mid += q;
cout << "+ " << q << endl;
int o;
cin >> o;
if(o >= mid / n){
l = mid;
}else{
r = mid;
}
}
cout << "! " << l << endl;
return 0;
}
G
给出若干条带权无向边,选择其中若干条边,使之构成一颗最小生成树,问最小的所有边的或和是多少
- 考虑每一位,从高到低,如果这一位可以去掉,那么把这一位包含的所有边都删掉;如果这一位不能去掉,那么答案应该包含这一位,然后考虑下一位,依次处理即可
#include <bits/stdc++.h>
using namespace std;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while(t--){
int n, m;
cin >> n >> m;
vector<tuple<int, int, int> > vs(m);
for(int i=0;i<m;i++){
int u, v, w;
cin >> u >> v >> w;
vs[i] = make_tuple(u, v, w);
}
int ans = 0;
vector<int> s(n + 1);
for(int i=30;i>=0;i--){
for(int j=1;j<=n;j++){
s[j] = j;
}
function<int(int)> FIND = [&](int x){
return x == s[x] ? x : s[x] = FIND(s[x]);
};
int cnt = 0;
for(auto j : vs){
int u = get<0>(j);
int v = get<1>(j);
int w = get<2>(j);
if(w & (1 << i)) continue;
u = FIND(u);
v = FIND(v);
if(u == v) continue;
s[u] = v;
cnt += 1;
}
if(cnt == n - 1){
vs.erase(remove_if(vs.begin(), vs.end(), [&](auto e){
return get<2>(e) & (1 << i);
}), vs.end());
}else{
ans |= (1 << i);
}
}
cout << ans << '\n';
}
return 0;
}