AtCoder Beginner Contest 356 A-E

A - Subsegment Reverse

题目大意:给出n, l, r要求输出1-n, 其中l-r翻转

分析:直接模拟即可。

代码:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <set>
#include <map>

using namespace std;

typedef pair<int, int> pii;
typedef long long ll;

const int N = 2e5 + 10;

int main()
{
    int n, l, r;
    cin >> n >> l >> r;
    for (int i = 1; i <= n; i ++ )
    {
        if (i == l)
        {
            for (int j = r; j >= l; j -- )
                cout << j << ' ';
            i = r;
        }
        else cout << i << ' ';
    }
    return 0;
}

B - Nutrients

题目大意:给出一个人的希望能够达到的多个健康值, 给出他吃的n个食物能够提供的多个健康值,问他是否达到所以的健康值要求。

分析:模拟即可

代码:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <set>
#include <map>

using namespace std;

typedef pair<int, int> pii;
typedef long long ll;

const int N = 2e5 + 10;

int a[N], b[N];

int main()
{
    int n, m;
    cin >> n >> m;
    for (int i = 0; i < m; i ++ )
        cin >> a[i];
    for (int i = 0; i < n; i ++ )
    {
        for (int j = 0; j < m; j ++ )
        {
            int x;
            cin >> x;
            b[j] += x;
        }
    }
    bool f = 1;
    for (int i = 0; i < m; i ++ )
        if (b[i] < a[i]) f = 0;
    if (f) cout << "Yes";
    else cout << "No";
    return 0;
}

C - Keys

题目大意:有N把钥匙,其中有真有假,插入至少K把真钥匙即可开锁。给出一系列插入情况以及对应的开锁情况,求可能的真假钥匙组合有多少种。

分析:可以看到数据非常小,所以可以考虑枚举所有的真假钥匙组合, 组合情况可以用状态压缩来快速处理,再判断所有情况是否符合。

时间复杂度:O(2^n * m * c)

代码:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <set>
#include <map>

using namespace std;

typedef pair<int, int> pii;
typedef long long ll;

const int N = 2e5 + 10;

int c[20];
int a[110][20];
bool f[110];

int main()
{
    int n, m, k;
    cin >> n >> m >> k;
    int res = 0;
    for (int i = 0; i < m; i ++ )
    {
        cin >> c[i];
        for (int j = 0; j < c[i]; j ++ )
            cin >> a[i][j];
        char op;
        cin >> op;
        if (op == 'o') f[i] = 1;
    }

    for (int i = 0; i < (1 << n); i ++ )//状态压缩,将i看成二进制数,若j位上为1则代表j这把钥匙为真钥匙,否则为假
    {
        bool flag = 1;//判断是否满足所有情况
        for (int j = 0; j < m && flag; j ++ )
        {
            int sum = 0;//记录真钥匙数量
            for (int v = 0; v < c[j]; v ++ )
                if (i >> (a[j][v] - 1) & 1)
                    sum ++ ;
            if (sum >= k && !f[j]) flag = 0;
            else if (sum < k && f[j]) flag = 0;
        }
        if (flag) res ++ ;
    }
    cout << res;
    return 0;
}

D - Masked Popcount

题目大意:给出N, M,求0-N所有数&M后的数转换成二进制总共有多少个1。

分析:我们可以考虑先将M拆分成二进制, 那么二进制位置上为1的才是有效的, 然后枚举这些位置, 判断0-N上有多少数在这些位置上为1,然后全部加起来。而判断有多少数可以先列举几个尝试,通过思考可以发现结果,细节可以看代码。

时间复杂度:O(logM)

代码:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <set>
#include <map>

using namespace std;

typedef pair<int, int> pii;
typedef long long ll;

const int N = 2e5 + 10;
const int mod = 998244353;

int main()
{
    ll n, m, res = 0;
    cin >> n >> m;
    for (int i = 0; i < 60; i ++ )
    {
        if (m >> i & 1)
        {
            ll pow1 = (ll)1 << i + 1;
            ll pow2 = pow1 >> 1;
            ll s = n % pow1;
            res = (res + max((ll)0, s - pow2 + 1)) % mod;
            res = (res + n / pow1 * pow2) % mod;
        }
    }
    cout << res;
    return 0;
}

E - Max/Min

题目大意:给出一个长度为n的数组,求所有的二元组{i, j}满足i<j,的max(a[i], a[j]) / min(a[i], a[j])向下取整的和。

分析:首先容易想到先将数组从大到小排序,然后答案就为从左边开始,除以每一个在它右边的数向下取整的和, 但是由于向下取整,会发现很难处理, 然后我们可以发现a[i]的范围在1e6, 那么我们可以考虑用桶s将数据存起来, 然后考虑时间复杂度,容易联想到nlogn的做法也就是n/1+ n/2 +n/3 +n/4+...+n/n这样,如果不考虑向下取整,那么答案为s[i]*sum(s[k], k为i的倍数*k/i)+s[i]*(s[i]-1)/2(1<=I<=1e6)考虑向下取整,我们可以将不是i的倍数数归到i的倍数里,由于i的倍数所含权值是根据大小,所有可以作桶的后缀和,那么从头加到尾即可,具体见代码。

时间复杂度:O(NlogN)

代码:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <set>
#include <map>

using namespace std;

typedef pair<int, int> pii;
typedef long long ll;

const int N = 1e6 + 10;

ll a[N];//桶
ll s[N];//桶的后缀和

int main()
{
    int n;
    cin >> n;
    for (int i = 0; i < n; i ++ )
    {
        int x;
        cin >> x;
        a[x] ++ ;
    }
    for (int i = N - 2; i; i -- )
        s[i] = s[i + 1] + a[i];

    ll res = 0;
    for (ll i = 1; i < N; i ++ )
    {
        res += (a[i] - 1) * a[i] / 2;//加上自身除自身的值
        res += (s[i] - a[i]) * a[i];//加上除i后等于1的数量乘以i的数量
        for (ll j = 2; j * i < N; j ++ )//除i后等于j
            res += a[i] * s[i * j];//这里加上的是除i后大于等于j的数量乘以i的数量,然后大于j的会在后面加上
    }
    cout << res;
    return 0;
}

赛时cwa了两发, 错在数组开小了,没注意M是偏大的,改了一次后没注意还要一个跟M相关,有点粗心了,f和g还有半小时,有思路但不多,这场感觉还行

  • 7
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值