文章目录
Codeforces Round #653 (Div. 3)
A. Required Remainder
题意: 给定三个数x , y , n ,求使得k % x = y , 0 ≤ k ≤ n 的最大的k。
题解: 找到一个k,使得(k - y) % x == 0,那么就是最大0 ~ k - y内的数字,且为x的倍数。那么我就是要找某个数字t范围内的x的最大倍数,那么我就t / x * x即可。因此就是 (n - y) / x * x + y
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int const MAXN = 2e5 + 10;
int n, m, T;
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
cin >> T;
while(T--) {
int x, y;
cin >> x >> y >> n;
cout << (n - y) / x * x + y << endl;
}
return 0;
}
B. Multiply by 2, divide by 6
题意: 在一次移动中,你可以将n乘以2或将n除以6(如果n被6整除,没有余数)。你的任务是找出从n获得1所需的最少移动次数,或者确定是否不可能做到这一点。你必须回答t个独立的测试案例。
题解: 按照整数分解定理,将n分解为 n = 2 k 1 ∗ 3 k 2 ∗ . . . n = 2 ^ {k1} * 3 ^ {k2} * ... n=2k1∗3k2∗... 如果不仅仅是2和3的幂次, 那么一定无法分解。同时有式子: n ∗ 2 t 1 = 6 t 2 n * 2 ^{t1} = 6 ^ {t2} n∗2t1=6t2。联立两个式子,可以得到: t 2 = k 2 , t 1 = t 2 − k 1 t2 = k2, t1 = t2 - k1 t2=k2,t1=t2−k1,计算即可
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int const MAXN = 2e5 + 10;
int n, m, T;
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
cin >> T;
while(T--) {
cin >> n;
int k1 = 0, k2 = 0;
while(n) {
if (n % 2 == 0) {
k1++;
n /= 2;
}
else break;
}
while(n) {
if (n % 3 == 0) {
k2++;
n /= 3;
}
else break;
}
int t2 = k2, t1 = t2 - k1;
if (t1 >= 0 && t2 >= 0 && n == 1) cout << t1 + t2 << endl;
else cout << -1 << endl;
}
return 0;
}
C. Move Brackets
题意: 给出一个长度为n的括号序列s,其中n为偶数(可除以2)。字符串 s 由 n2 个开括号 ‘(’ 和 n2 个收括号 ‘)’ 组成。在一次移动中,你可以准确地选择一个括号,并将其移动到字符串的开头或结尾(即选择某个索引i,删除s的第i-th个字符,并将其插入s的所有剩余字符之前或之后)。你的任务是找到从s中获得正则括号序列所需的最少移动次数。可以证明,在给定的约束条件下,答案总是存在的。
题解: 因为答案一定存在,因此,如果当前括号适配,那么我直接把右括号直接移动到最尾部即可
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
int const MAXN = 2e5 + 10;
int n, m, T;
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
cin >> T;
while(T--) {
string s;
cin >> n >> s;
int res = 0;
stack<char> stk;
for (int i = 0; i < s.size(); ++i) {
if (s[i] == '(') stk.push('(');
else {
if (!stk.empty() && stk.top() == '(') stk.pop();
else res++;
}
}
cout << res << endl;
}
return 0;
}
D. Zero Remainder Arrayz
题意:
给你一行数组,分别进行一下两种操作:
Choose exactly one i from 1 to n and increase ai by x (ai:=ai+x), then increase x by 1 (x:=x+1).
Just increase x by 1 (x:=x+1).
在给你一个k,求需要最小步骤的操作使元素中的每一个元素都能被k整除。
题解: 一个数字必然去找离他最近的k的倍数,如果这个数字被使用过,那么就找下一个k的倍数。然后一个问题就是比如现在有3个1,那么我第一个1找k,第二个1就得找k+k,第三个1就得找3 * k,因此需要的差值为:k - 1 + t * k
代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+10;
int t,n;
ll a[N],b[N],ans,k;
bool cmp(ll a,ll b) {
return a>b;
}
int main() {
scanf("%d",&t);
while(t--) {
cin>>n>>k;
memset(a,0,sizeof a);
memset(b,0,sizeof b);
for(int i=1;i<=n;i++) {
cin>>a[i];
a[i]%=k;
}
sort(a+1,a+1+n,cmp);//从大到小进行排序
ll ans=0;
for(int i=1;i<=n;i++) {
if(a[i]==0)break;
if(a[i]!=a[i-1])//不同就直接加上元素到k的步骤
b[i]=k-a[i];
else {
b[i]=b[i-1]+k;//相同就直接加上前一个差值+k
}
ans=max(ans,b[i]+1);//+1是因为每次操作都是从2. Just increase x by 1 (x:=x+1).开始的。
}
printf("%lld\n",ans);
}
return 0;
}
E1.Reading Books (easy version)
题意: 一共有n本书,第i本书需要花费ti时间阅读。对于每本书,如果a[i] = 1,则A喜欢,否则A不喜欢;如果b[i] = 1, 则B喜欢,否则B不喜欢。现在希望A和B都至少阅读k本喜欢的书,问最少的花费时间是多少?
题解: 贪心。先找出A和B都喜欢的书,这些书按照阅读时间从小到大排序。然后找到仅仅只有A喜欢的书和仅仅只有B喜欢的书,将这些书进行组合,找出剩余的前几小即可
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
int const MAXN = 2e5 + 10;
int n, m, T, k;
vector<int> a, b, c;
int main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
cin >> n >> k;
for (int i = 1, t, k1, k2; i <= n; ++i) {
cin >> t >> k1 >> k2;
if (k1 && k2) a.push_back(t);
else if (k1) b.push_back(t);
else if (k2) c.push_back(t);
}
sort(b.begin(), b.end()), sort(c.begin(), c.end());
for (int i = 0; i < min(b.size(), c.size()); ++i) a.push_back(b[i] + c[i]);
if (a.size() < k) puts("-1");
else {
sort(a.begin(), a.end());
LL res = 0;
for (int i = 0; i < k; ++i) res += a[i];
cout << res << endl;
}
return 0;
}
E2. Reading Books (hard version)
题意: 一共有n本书,第i本书需要花费ti时间阅读。对于每本书,如果a[i] = 1,则A喜欢,否则A不喜欢;如果b[i] = 1, 则B喜欢,否则B不喜欢。现在希望A和B都至少阅读k本喜欢的书,且一共需要取m本书,问最少的花费时间是多少?
题解: 令 num 为当前 a = 1, b = 1 的书的数量,ans 为正解的 a = 1, b = 1 的书的数量。
- 当num < ans时,会多拿 a = 1, b = 0 的书以及 a = 0, b = 1 的书。num越小,拿的越多,最终用时就越多。
- 当num > ans时,会多拿 a = 1, b = 1 的书。num越大,拿的越多,最终用时就越多。
然后就可以三分。(不过感觉太牵强了)
代码:
#include <bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;
int const MAXN = 2e5 + 10;
int n, m, T, k;
vector<PII> a, b, ab, other;
vector<int> ans;
int check(int mid) {
// 无解的情况
if ((int)a.size() < k - mid) return 2e9 + 11;
if ((int)b.size() < k - mid) return 2e9 + 11;
if (mid + max(0, k - mid) * 2 > m) return 2e9 + 11;
int need = m - mid, res = 0; // need记录需要的书籍数量
ans.clear();
vector<pair<int, int>> vec; // vec存储还需要取的书籍
for (int i = 0; i < mid; i++) res += ab[i].first, ans.push_back(ab[i].second);
for (int i = mid; i < ab.size(); i++) vec.push_back(ab[i]);
for (int i = 0; i < k - mid; i++)
res += a[i].first + b[i].first, need -= 2,
ans.push_back(a[i].second), ans.push_back(b[i].second);
for (int i = max(0, k - mid); i < a.size(); i++) vec.push_back(a[i]);
for (int i = max(0, k - mid); i < b.size(); i++) vec.push_back(b[i]);
for (int i = 0; i < other.size(); i++) vec.push_back(other[i]);
sort(vec.begin(), vec.end());
for (int i = 0; i < need && i < vec.size(); i++) res += vec[i].first, ans.push_back(vec[i].second); // 从还需要取的书籍里面取need个
return res;
}
signed main() {
ios_base::sync_with_stdio(false);
cin.tie(NULL);
cin >> n >> m >> k;
for (int i = 1, x1, x2, x3; i <= n; ++i) {
cin >> x1 >> x2 >> x3; // 将对应的存入书本情况存储起来
if (x2 && x3) ab.push_back({x1, i});
else if (x2) a.push_back({x1, i});
else if (x3) b.push_back({x1, i});
else other.push_back({x1, i});
}
sort(a.begin(), a.end()), sort(b.begin(), b.end()), sort(ab.begin(), ab.end()), sort(other.begin(), other.end()); // 全部按照花费从小到大排序
// 三分查找最佳的ab值
int l = 0, r = min((int)ab.size(), m);
while(r - l > 10){
int midl = l + (r - l) / 3, midr = r - (r - l) / 3;
if (check(midl) < check(midr)) r = midr; // 求凸性函数,如果求凹形,那么改为l = midl
else l = midl;
}
int res = 2e9 + 10, Min_idx = -1;
for (int i = l; i <= r; i++) { // 找到最值
int tmp = check(i);
if (tmp < res) res = tmp, Min_idx = i;
}
if (Min_idx == -1) return puts("-1"), 0;
cout << check(Min_idx) << endl;
for (auto a : ans) cout << a << " ";
return 0;
}
F.Cyclic Shifts Sorting
题意: 给定一个长度为n的数组,每次操作可以把一个长度为3的区间的右旋转一格,问是否能够在n * n次操作内使得一个序列变得有序
题解: 使得一个序列变得有序,可以第一次选择1使得有序,第2次选择2变得有序…。也可以第一次选择n,第二次选择n - 1, …。这两种情况都处理下,最后判断一下序列是否变得有序即可。
代码:
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 505;
int a[MAXN];
int t;
int n;
int tot = 0;
vector<int> ans;
void ratate(int u)
{
//旋转以u为起点的三元组
ans.push_back(u);
tot++;
int t1 = a[u];
int t2 = a[u + 1];
int t3 = a[u + 2];
a[u] = t3;
a[u + 1] = t1;
a[u + 2] = t2;
}
int main()
{
scanf("%d", &t);
while (t--)
{
ans.clear();
tot = 0;
scanf("%d", &n);
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
for (int i = 1; i <= n - 2; i++)
{
int u;
int Min = 1e9;
for (int j = i; j <= n; j++)
{
if (a[j] < Min)
Min = a[j], u = j;
}
while (u >= i + 2)
ratate(u - 2), u -= 2;
if (u == i + 1)
{
ratate(i);
ratate(i);
continue;
}
}
for (int i = n; i >= 3; i--)
{
int u;
int Max = -1;
for (int j = i; j >= 1; j--)
{
if (a[j] > Max)
Max = a[j], u = j;
}
while (u <= i - 2)
ratate(u), ratate(u), u += 2;
if (u == i - 1)
{
ratate(i - 2);
continue;
}
}
for (int i = 1; i < n; i++)
if (a[i] > a[i + 1])
tot = -1;
printf("%d\n", tot);
if (tot != -1)
{
for (int i = 0; i < ans.size(); i++)
printf("%d ", ans[i]);
printf("\n");
}
}
}