Codeforces Round #653 (Div. 3)

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=2k13k2... 如果不仅仅是2和3的幂次, 那么一定无法分解。同时有式子: n ∗ 2 t 1 = 6 t 2 n * 2 ^{t1} = 6 ^ {t2} n2t1=6t2。联立两个式子,可以得到: t 2 = k 2 , t 1 = t 2 − k 1 t2 = k2, t1 = t2 - k1 t2=k2,t1=t2k1,计算即可

代码:

#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 的书的数量。

  1. 当num < ans时,会多拿 a = 1, b = 0 的书以及 a = 0, b = 1 的书。num越小,拿的越多,最终用时就越多。
  2. 当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");
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值