AtCoder Beginner Contest 169

比赛链接:https://atcoder.jp/contests/abc169/tasks

A - Multiplication 1

#include <bits/stdc++.h>
using namespace std;
int main() {
    int a, b; cin >> a >> b;
    cout << a * b << "\n";
}

B - Multiplication 2

题意

计算 $a_1 \times a_2 \times ... \times a_n$ 。($2≤n≤10^5, 0≤ a_i ≤ 10^{18}$) 

题解一

若 $ans$ 乘以某个 $a_i$ 大于 $10^{18}$ 则溢出,即:

$ans \times a_i > 10^{18}$,

为避免运算过程中乘积溢出,移项得:$a_i > \frac{10^{18}}{ans}$ 。

代码

#include <bits/stdc++.h>
using ll = long long;
using namespace std;
const ll MAXN = 1e18;
int main() {
    int n; cin >> n;
    ll a[n] = {};
    for (int i = 0; i < n; i++)
        cin >> a[i];
    if (count(a, a + n, 0)) {
        cout << 0 << "\n";
        return 0;
    }
    ll ans = 1;
    for (int i = 0; i < n; i++) {
        if (a[i] > MAXN / ans) {
            cout << -1 << "\n";
            return 0;
        }
        ans *= a[i];
    }
    cout << ans << "\n";
}

题解二

转化为 __int128 可以避免溢出。

代码

#include <bits/stdc++.h>
using ll = long long;
using namespace std;
const ll MAXN = 1e18;
int main() {
    int n; cin >> n;
    ll a[n] = {};
    for (int i = 0; i < n; i++)
        cin >> a[i];
    if (count(a, a + n, 0)) {
        cout << 0 << "\n";
        return 0;
    }
    ll ans = 1;
    for (int i = 0; i < n; i++) {
        if ((__int128)ans * a[i] > MAXN) {
            cout << -1 << "\n";
            return 0;
        }
        ans *= a[i];
    }
    cout << ans << "\n";
}

题解三

如果能由下一个积除以 $a_i$ 得到现在的积说明不会发生溢出,但是这个方法需要用 $unsigned\ long\ long$ 才行。

代码

#include <bits/stdc++.h>
using ull = unsigned long long;
using namespace std;
const ull MAXN = 1e18;
int main() {
    int n; cin >> n;
    ull a[n] = {};
    for (int i = 0; i < n; i++)
        cin >> a[i];
    if (count(a, a + n, 0)) {
        cout << 0 << "\n";
        return 0;
    }
    ull ans = 1;
    for (int i = 0; i < n; i++) {
        ull next = ans * a[i];
        if (next / a[i] != ans or next > MAXN) {
            cout << -1 << "\n";
            return 0;
        }
        ans *= a[i];
    }
    cout << ans << "\n";
}

题解四

使用 python 中自带的大数运算。

代码

def main():
    N = int(input())
    A = list(map(int, input().split()))

    if 0 in A:
        print(0)
        return

    ans = 1
    for a in A:
        ans *= a
        if ans > int(1e18):
            print(-1)
            return

    print(ans)

main()

C - Multiplication 3

题意

$a$ 为整数,$b$ 为保留小数点后两位的小数,计算 $a \times b$ 。($0≤a≤10^{15}, 0≤ b < 10$) 

题解

把 $b$ 转化为整数再计算。

代码

#include <bits/stdc++.h>
using ll = long long;
using namespace std;
int main() {
    ll a; string s; cin >> a >> s;
    s.erase(1, 1);
    ll b = stoll(s);
    cout << a * b / 100;
}

D - Div Game

题意

给出一个正整数 $n$,每次操作可以选择一个正整数 $z$,要求:

  • $z = p^e$,$p$ 为正素数,$e$ 为正数
  • $z$ 整除 $n$
  • $z$ 不同于任一之前选择的 $z$
  • 如果以上条件满足,$n = \frac{n}{z}$

找出最多可以进行多少次操作。

题解

找出每个质因数的个数,依次减去 $1,2,3...$ 即可。

代码

#include <bits/stdc++.h>
using ll = long long;
using namespace std;
int main() {
    ll n; cin >> n;
    map<ll, ll> mp;
    for (ll i = 2; i * i <= n; i++) {
        while (n % i == 0) {
            ++mp[i];
            n /= i;
        }
    }
    if (n != 1) ++mp[n];
    ll ans = 0;
    for (auto i : mp) {
        for (int j = 1; j <= i.second; j++) {
            i.second -= j;
            ++ans;
        }
    }
    cout << ans << "\n";
}

E - Count Median

题意

有 $n$ 个数 $x_1,x_2,...,x_n$,$a_i≤x_i≤b_i$,找出中值 $x_{mid}$ 可能的值的个数。

  • 中值为将所有 $x_i$ 排序,位于中间的 $x_i$
  • 如果 $n$ 为奇数,中值为 $x_{(n+1)/2}$
  • 如果 $n$ 为偶数,中值为 $(x_{n/2} + x_{n/2 + 1}) / 2$

题解

中值的最小值为 $a_i$ 的中值,最大值为 $b_i$ 的中值,中值的个数即:$b_{mid} - a_{mid} + 1$ 。

代码

#include <bits/stdc++.h>
using namespace std;
int main() {
    int n; cin >> n;
    int a[n] = {}, b[n] = {};
    for (int i = 0; i < n; i++)
        cin >> a[i] >> b[i];
    sort(a, a + n);
    sort(b, b + n);
    int mi = 0, mx = 0;
    if (n & 1) {
        mi = a[n / 2];
        mx = b[n / 2];
    } else {
        mi = a[n / 2 - 1] + a[n / 2];
        mx = b[n / 2 - 1] + b[n / 2];
    }
    cout << mx - mi + 1;
}

F - Knapsack for All Subsets

题意

有 $n$ 个数 $a_1,a_2,...,a_n$,计算 ${1,2,...,n}$ 的 $2^n - 1$ 个非空子集有多少子集作为下标求和可以得到 $s$ 。

题解

$dp_i$ 表示和为 $i$ 的集合共有多少个。

每次添加一个数 $x$,会产生两种变化:

  • $x$ 与 $dp_i$ 中的每个集合构成的新集合都可以构成 $i + x$:$dp_{i + x}\ +=\ dp_i$
  • $x$ 与 $dp_i$ 中的每个集合构成了一个包含原先集合的新集合:$dp_i\ *=\ 2$

代码

#include <bits/stdc++.h>
using namespace std;
const int mod = 998244353;
int dp[6010];
int main() {
    int n, s; cin >> n >> s;
    dp[0] = 1;
    for (int i = 0; i < n; i++) {
        int x; cin >> x;
        for (int j = s; j >= 0; j--) {
            (dp[j + x] += dp[j]) %= mod;
            (dp[j] *= 2) %= mod;
        }
    }
    cout << dp[s] << "\n";
}

 

posted @ 2020-06-01 18:00  Kanoon  阅读(298)  评论(0编辑  收藏  举报