Codeforces Round #813 (Div. 2) A-C、E1

A.Wonderful Permutation

题意:给定一个长度为n的排列和一个数字k,每次操作可以交换两个数,问最少需要几次操作使得前k个数的和最小

题解

由于给定的是1-n的排列,所以要使得前k个数的和最小,肯定是让1-k排在前k个位置,那么统计一下前k个数中大于k的数的个数即可

代码实现

#include <algorithm>
#include <iostream>
#include <stdio.h>
#include <iomanip>
#include <cstring>
#include <string>
#include <vector>
#include <queue>
#include <set>
#include <map>
#define ll long long
using namespace std;
int t, n, k;
int a[110];
int main(){
    cin >> t;
    while(t--){
        cin >> n >> k;
        for(int i = 1; i <= n; i++) cin >> a[i];
        int ans = 0;
        for(int i = 1; i <= k; i++){
            if(a[i] > k) ans++;
        }
        cout << ans << "\n";
    }
    return 0;
}

B.Woeful Permutation

题意:构造一个1-n的排列p,使得 ∑ i = 1 n l c m ( p [ i ] , i ) \sum_{i=1}^{n}lcm(p[i], i) i=1nlcm(p[i],i)最大

题解

首先我们知道,lcm(x, y) <= x * y,那么我们肯定尽可能的让所有的lcm(p[i], i)都等于p[i]*i,也就是让p[i]和i互质,假设一开始p[i] = i,那么一个简单的思路就是交换相邻的两项,因为相邻的两项一定是互质的。
接下来再思考一个问题,交换相邻两项一定最优吗。太严谨的证明我也不会,下面举个小例子主观上感受一下。
对于四个数,p[2] = 2,p[3] = 3,p[4] = 4,p[5] = 5,按照两个数互质的想法进行选择,可以得到3,2,5,4和5,4,3,2两种序列,很明显前面一种方法结果更优。
我们可以倒着想,对于最后一个数p[n] = n,如果让我们在1-n-1里找一个数p[i] = i进行交换使得lcm(i, p[n]) + lcm(n, p[i])最大,那么肯定选择p[n - 1] = n - 1,然后这两个数就定下来了,接下来继续从n-2开始,这样一直倒着往前进行交换,其实就是交换相邻两项的过程。

代码实现

#include <algorithm>
#include <iostream>
#include <stdio.h>
#include <iomanip>
#include <cstring>
#include <string>
#include <vector>
#include <queue>
#include <set>
#include <map>
#define ll long long
using namespace std;
int t, n;
int main(){
    cin >> t;
    while(t--){
        cin >> n;
        if(n % 2 == 0){
            for(int i = 1; i <= n; i += 2){
                cout << i + 1 << " " << i << " ";
            }
            cout << "\n";
        }else{
            cout << 1 << " ";
            for(int i = 2; i <= n; i += 2){
                cout << i + 1 << " " << i << " ";
            }
            cout << "\n";
        }
    }
    return 0;
}

C.Sort Zero

题意:给定一个长度为n的正整数序列,每次操作可以选择一个数x,然后将所有等于x的数变为0,问最少要进行几次操作才能让序列单调不减。

题解

首先要满足单调不减,那么对于a[i] > a[i + 1]这种情况,我们需要将所有等于a[i]的数变为0,一旦a[i]这个位置变成了0,那之前所有的数也要变成0,因为要满足单调不减,所以我们直接找到最后一个a[i] > a[i + 1]的位置pos,在这个pos之前的数都要变为0(包括a[pos]),但是这样可能会导致pos之后也会出现0,因为后面可能会出现某些数等于前面需要变成0的数,所以最后我们需要找的其实是最后一个0出现的位置poss,这是因为我们之前的操作已经保证了poss之后的数肯定是单调不减的,统计一下poss之前不同数的个数就是答案

代码实现

#include <algorithm>
#include <iostream>
#include <stdio.h>
#include <iomanip>
#include <cstring>
#include <string>
#include <vector>
#include <queue>
#include <set>
#include <map>
#define ll long long
using namespace std;
const int N = 1e5 + 50;
int t, n;
int a[N], vis[N], f[N], b[N];
int main(){
    cin >> t;
    while(t--){
        memset(vis, 0, sizeof(vis));
        memset(f, 0, sizeof(f));
        cin >> n;
        for(int i = 1; i <= n; i++){
            cin >> a[i];
            b[i] = a[i];
        }
        int pos = 0;
        for(int i = 1; i < n; i++){
            if(a[i] > a[i + 1]) pos = i;
        }
        for(int i = 1; i <= n; i++){
            if(i <= pos){
                vis[a[i]] = 1;
                a[i] = 0;
            }
            if(vis[a[i]]) a[i] = 0;
        }
        int poss = 0;
        for(int i = n; i >= 1; i--){
            if(a[i] == 0){
                poss = i;
                break;
            }
        }
        int ans = 0;
        for(int i = 1; i <= poss; i++){
            if(!f[b[i]]){
                f[b[i]] = 1;
                ans++;
            }
        }
        cout << ans << "\n";
    }
    return 0;
}

E1.LCM Sum (easy version)

题意:给定两个正整数l,r,求出满足l ≤ \leq i < j < k ≤ \leq r且lcm(i, j, k) ≥ \geq i + j + k的三元组(i, j, k)的个数

题解

直接求解情况比较复杂,我们可以采取正难则反的策略,求出lcm(i, j, k) < i + j + k的三元组个数,然后用总数减去它。
首先总数很好求,[l, r]区间总共有n = r - l + 1个数,所有三元组的个数为这n个数中任选3个数的组合数,即n * (n - 1) * (n - 2) / 6
然后对于lcm(i, j, k) < i + j + k的情况,其实就分为两种:lcm(i, j, k) = k 和 lcm(i, j, k) = 2k,因为k < i + j + k < 3 * k且lcm(i, j, k)为k的倍数
对于第一种情况,需要满足的条件是i和j为k的小于k的因子,可以先预处理出[l, r]中k的小于k的因子的个数,计为p[k],那么个数就是这些因子中任取两个的组合数,即p[k] * (p[k] - 1) / 2
对于第二种情况,需要满足的条件是i和j为2k的因子,且i + j > k,这种情况其实是可以分析出i和j的取值的。
因为j是2k的因子,所以可以设j = 2k / x,又有k / 2 < j < k (i + j > k 且 i < j),则k / 2 < 2k / x < k,解得2 < x < 4,所以x = 3,即j = 2k / 3
因为i + j > k且j = 2k / 3,所以i > k / 3,因为i是2k的因子,所以可以设i = 2k / x,又有k / 3 < i < j = 2k / 3,则k / 3 < 2k / x < 2k / 3,解得3 < x < 6,所以x = 4或5,即i = k / 2或 2k / 5
最后,我们只需要枚举k,然后让答案减去上述两种情况的方案数即可。

代码实现

#include <algorithm>
#include <iostream>
#include <stdio.h>
#include <iomanip>
#include <cstring>
#include <string>
#include <vector>
#include <queue>
#include <set>
#include <map>
#define ll long long
using namespace std;
const int N = 2e5 + 50;
int t, l, r;
ll p[N];
int main(){
    cin >> t;
    while(t--){
        memset(p, 0, sizeof(p));
        cin >> l >> r;
        ll n = r - l + 1;
        ll ans = n * (n - 1) * (n - 2) / 6;
        for(int i = l; i < r; i++)
            for(int j = 2 * i; j <= r; j += i) p[j]++;
        for(int i = l + 2; i <= r; i++){
            ll tmp = p[i] * (p[i] - 1) / 2;
            ans -= tmp;
            if(i % 3 == 0){
                if(i % 2 == 0 && i / 2 >= l) ans--;
                if(i % 5 == 0 && i / 5 * 2 >= l) ans--;
            }
        }
        cout << ans << "\n";
    }
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值