A. Be Positive
题目概述:给你一个大小为n的数组a,数组仅由-1,0,1组成,每次可以选择一个数字加1,求出最小操作次数使得整个数组的所有元素乘积为正
思考过程:对每个0都必须使用一次操作变为1,对于1不用管,-1的话若为偶数个则不用管,奇数个则次数+2变为1
算法:构造
时间复杂度:O(n)
代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define ef(a, b, i) for (int i = a; i <= b; i++)
#define nf(a, b, i) for (int i = a; i < b; i++)
#define rf(a, b, i) for (int i = a; i >= b; i--)
#define endl '\n'
typedef pair<int, int> PII;
#define Hope_AC ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
const int N = 2e5 + 10;
int t;
int n;
inline void work()
{
cin >> n;
int num_1 = 0,res = 0;
ef(1,n,i)
{
int x;
cin >> x;
if(x==0)
res++;
if(x==-1)
num_1++;
}
if(num_1&1)
res+=2;
cout << res << endl;
}
signed main()
{
Hope_AC;
cin >> t;
while (t--)
{
work();
}
return 0;
}
B. Unconventional Pairs
题目概述:给你一个偶数n和n个数字,需要将这些数字两两配对,计算出每个配对的差值,使得整体的最大差值最小,输出这个差值
思考过程:要想使得整体的差值最小化,必须使得每个的差值最小化,不妨将数组进行排序,这样可以保证对于每个数而言,与之差值最小的数就在旁边,然后输出答案即可
算法:构造,排序
时间复杂度:O(n)
代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define ef(a, b, i) for (int i = a; i <= b; i++)
#define nf(a, b, i) for (int i = a; i < b; i++)
#define rf(a, b, i) for (int i = a; i >= b; i--)
#define endl '\n'
typedef pair<int, int> PII;
#define Hope_AC ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
const int N = 2e5 + 10;
int t;
int n;
inline void work()
{
cin >> n;
vector<int> v(n);
nf(0,n,i)cin>>v[i];
sort(v.begin(),v.end());
int res = -0x3f3f3f3f;
nf(0,n,i)
{
if(i&1)
{
res = max(res, abs(v[i] - v[i - 1]));
}
}
cout << res << endl;
}
signed main()
{
Hope_AC;
cin >> t;
while (t--)
{
work();
}
return 0;
}
C. MEX rose
题目概述:给你一个长度为n的数组a和一个数字k,其0<=k<=n,你每次可以选择数组中的一个数,使其变为[0,n]的任何一个数,问最少修改多少次能使得整个数组的MEX变为k
思考过程:对于数组中a[i]==k的数来说,这些数是必须修改的,我们将其设为w1,我们可以统计<k的数字还需要添加多少个,我们将其设为w2,如果w1>=w2,那么答案为w1,否则为w2
算法:贪心
时间复杂度:O(n)
代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define ef(a, b, i) for (int i = a; i <= b; i++)
#define nf(a, b, i) for (int i = a; i < b; i++)
#define rf(a, b, i) for (int i = a; i >= b; i--)
#define endl '\n'
typedef pair<int, int> PII;
#define Hope_AC ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
const int N = 2e5 + 10;
int t;
int n, k;
int a[N];
inline void work()
{
cin >> n >> k;
ef(1,n,i)cin>>a[i];
unordered_map<int, int> um;
ef(1,n,i)
{
um[a[i]]++;
}
int w = 0;
nf(0,k,i)
{
if(um[i]==0)
w++;
}
if(um[k]>=w)
cout << um[k] << endl;
else
cout << w << endl;
}
signed main()
{
Hope_AC;
cin >> t;
while (t--)
{
work();
}
return 0;
}
D. A and B
题目概述:给你一个长度为n的字符串s,其只由a和b两种字符组成,每次你可以选择两个相邻的字符进行交换,问最少多少次交换可以使得整体的s成为一个aaabbbaaa或bbbaaabbb或aaabbb这样的区块形式
思考过程:转化题意:我们需要把其中一种字符变成一个区块,然后我们可以对整体进行一个推导
写法1:

代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define ef(a, b, i) for (int i = a; i <= b; i++)
#define nf(a, b, i) for (int i = a; i < b; i++)
#define rf(a, b, i) for (int i = a; i >= b; i--)
#define endl '\n'
typedef pair<int, int> PII;
#define Hope_AC ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
const int N = 2e5 + 10;
int t;
int n;
string s;
inline void work()
{
cin >> n;
cin >> s;
s = ' ' + s;
vector<int> apos;
vector<int> bpos;
ef(1,n,i)
{
if(s[i]=='a')
apos.emplace_back(i);
else
bpos.emplace_back(i);
}
int res1 = 0, res2 = 0;
if(apos.size()>=2)
{
int mid = apos.size() / 2;
int pos = apos[mid] - mid;
nf(0,apos.size(),i)
{
res1 += abs(pos + i - apos[i]);
}
}
if (bpos.size() >= 2)
{
int mid = bpos.size() / 2;
int pos = bpos[mid] - mid;
nf(0, bpos.size(), i)
{
res2 += abs(pos + i - bpos[i]);
}
}
cout << min(res1,res2) << endl;
}
signed main()
{
Hope_AC;
cin >> t;
while (t--)
{
work();
}
return 0;
}
写法2:将左右两端的字符往中间的字符移动,同样用到了中位数的原理
代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define ef(a, b, i) for (int i = a; i <= b; i++)
#define nf(a, b, i) for (int i = a; i < b; i++)
#define rf(a, b, i) for (int i = a; i >= b; i--)
#define endl '\n'
typedef pair<int, int> PII;
#define Hope_AC ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
const int N = 2e5 + 10;
int t;
int n;
string s;
inline void work()
{
cin >> n;
cin >> s;
s = ' ' + s;
vector<int> apos;
vector<int> bpos;
ef(1,n,i)
{
if(s[i]=='a')
apos.emplace_back(i);
else
bpos.emplace_back(i);
}
int res1 = 0, res2 = 0;
if(apos.size()>=2)
{
int mid = apos.size() / 2;
int w = 1;
rf(mid-1,0,i)
{
res1 += abs(apos[mid] - w - apos[i]);
w++;
}
w = 1;
ef(mid+1,apos.size()-1,i)
{
res1 += abs(apos[mid] + w - apos[i]);
w++;
}
}
if (bpos.size() >= 2)
{
int mid = bpos.size() / 2;
int w = 1;
rf(mid - 1, 0, i)
{
res2 += abs(bpos[mid] - w - bpos[i]);
w++;
}
w = 1;
ef(mid + 1, bpos.size() - 1, i)
{
res2 += abs(bpos[mid] + w - bpos[i]);
w++;
}
}
cout << min(res1,res2) << endl;
}
signed main()
{
Hope_AC;
cin >> t;
while (t--)
{
work();
}
return 0;
}
算法:字符串构造
时间复杂度:O(n)
E. Hidden Knowledge of the Ancients
题目概述:给你四个数字n,k,l,r和一个长度为n的数组,要求出有多少个不同的区间使得长度为[l,r]的时候恰好包含k个不同的数
思考过程:观察数据量我们需要一个O(n)或者O(nlogn)左右的算法,考虑区间问题很容易想到双指针,我们可以从头到尾遍历右区间r,然后对于每个右区间的答案就是他对应的左区间的合理范围,这部分我们可以通过map来实现对数字出现次数的统计
注意:这里的map要使用红黑树map而不是哈希表为底层的unordered_map,被这个问题卡了好久),因为尽管理想情况下哈希表会更快,但是如果遇到极端的哈希冲突,时间复杂度会提高到O(n),使得整体的时间复杂度提高到O(n^2)的水平,而map平均为logn,正好可以使得整体时间复杂度为O(nlogn),这道题的数据恰好hack了极端的哈希冲突,所以必须使用map来存
算法:双指针,枚举
时间复杂度:O(nlogn)
代码:
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define ef(a, b, i) for (int i = a; i <= b; i++)
#define nf(a, b, i) for (int i = a; i < b; i++)
#define rf(a, b, i) for (int i = a; i >= b; i--)
#define endl '\n'
typedef pair<int, int> PII;
#define Hope_AC ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
const int N = 2e5 + 10;
int t;
int n,k,L,R;
int a[N];
map<int, int> mp;
int numl[N], numr[N]; //符合条件下 numl:对应r的l最左边,numr:对应r的l最右边
inline void work()
{
cin >> n >> k >> L >> R;
ef(1, n, i) cin >> a[i];
mp.clear();
memset(numl, 0, sizeof numl);
memset(numr,0,sizeof numr);
//预处理numl
int l = 1;
ef(1,n,r)
{
mp[a[r]]++;
while(mp.size()>k)
{
mp[a[l]]--;
if(mp[a[l]]==0)
{
mp.erase(a[l]);
}
l++;
}
if(mp.size()==k)
numl[r] = l;
}
//预处理numr
mp.clear();
l = 1;
ef(1, n, r)
{
mp[a[r]]++;
while (mp.size() >= k)
{
mp[a[l]]--;
if (mp[a[l]] == 0)
{
mp.erase(a[l]);
}
l++;
}
if(l>1)
{
l--;
mp[a[l]]++;
if(mp.size()==k)
{
numr[r] = l;
}
}
}
int res = 0;
ef(1,n,i)
{
int lmm = max(numl[i],i-R+1);
int lma = min(numr[i], i - L + 1);
if(lmm<=lma&&numl[i]&&numr[i])
{
res += lma - lmm + 1;
}
}
cout<<res<<endl;
}
signed main()
{
Hope_AC;
cin >> t;
while (t--)
{
work();
}
return 0;
}
F. Nezuko in the Clearing
题目概述:给你两个数字h和d,表示一个人的初始生命值和终点位置,从起点0出发,每次经过一个节点可以选择休息或者前进,休息加一点生命值,前进消耗第i次连续前进值的生命值,每次操作增加一个回合数量,问最少需要多少回合才能到达终点
思考过程:从数据范围来看需要一个方法可以快速搜索答案,而对于一个位置而言,休息一次再走一次必然优于连续休息两次,所以每个点位最多只能休息一次,而对于每次前进,我们可以将路程尽可能的拆成平均的线段,这样我们就可以在指定的休息次数下保证走完全程,而休息次数我们用二分来查找
算法:二分搜索,数学
时间复杂度:O(logd)
代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define ef(a, b, i) for (int i = a; i <= b; i++)
#define nf(a, b, i) for (int i = a; i < b; i++)
#define rf(a, b, i) for (int i = a; i >= b; i--)
#define endl '\n'
typedef pair<int, int> PII;
#define Hope_AC ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
const int N = 2e5 + 10;
int t;
int h, d;
bool check(int x)
{
int num = x + 1;
int w = d / num;
int s = d % num;
int need = s * (w + 1) * (w + 2) / 2 + (num - s) * w * (w + 1) / 2;
return need < h + x;
}
inline void work()
{
cin >> h >> d;
int l = 0, r = d + 1;
while(l<r)
{
int mid = l + r >> 1;
if(check(mid))
{
r = mid;
}
else
{
l = mid + 1;
}
}
cout << d + l << endl;
}
signed main()
{
Hope_AC;
cin >> t;
while (t--)
{
work();
}
return 0;
}
2241



被折叠的 条评论
为什么被折叠?



