Atcoder Beginner Contest 258 (D, E, G)

D - Trophy

\(N\) 个关卡,通过每关需要 \(A_i + B_i\) 时间,扫荡需要 \(B_i\) 时间,必须通过后才能扫荡,问一共打完 \(X\) 关最少需要多长时间

\(1 \le N\le 2 \times 10^5\)

\(1\le A[i],B[i] \le 10^9 (1\le i \le N)\)

\(1\le X \le 10^9\)

考虑以每一关为结尾,维护到这一关的最小扫荡时间 \(v\),当前贡献是 \(\sum_{i=1} ^ {N} (A[i] + B[i]) + (X-i) \times v\) 取最小

代码

typedef long long ll;
const int N = 2e5 + 10;
ll a[N], b[N];
void solve() {
    int n, x;
    cin >> n >> x;
    for (int i = 1; i <= n; i++) cin >> a[i] >> b[i];
    ll mn = 1e18;
    ll ans = 1e18 + 2e9, sum = 0;
    for (int i = 1; i <= min(n, x); i++) {
        ll cnt = x - i;
        sum += a[i] + b[i];
        mn = min(mn, b[i]);
        ans = min(ans, sum + mn * cnt);
    }
    cout << ans << '\n';
}

E - Packing Potatoes

给定一个序列 \(W\) 长度为 \(N\)\(W_i\) 代表土豆的重量,现在有一条传送带,按照 \(W\) 序列 依次把土豆放在传送带上(\(W_0,W_1,...W_{n-1}, W_0...\)),传送带下面有一个麻袋来接土豆,如果麻袋内土豆的总重量大于等于 \(X\) 的话,暂停运输,换一个空的麻袋继续来装。

给定 \(Q\) 次询问,每次给定一个正整数 \(K_i\) ,问第 \(K_i\) 个麻袋里面装了多少个土豆。

\(1\le N,Q \le 2\times 10^5\) \(1\le X \le 10^9\) \(1\le W_i \le 10^9 (0\le i \le N-1)\)

\(1\le K_i \le 10^{12}\) 保证所有输入都是正整数

考虑到从每个点开始装到装满的的点是固定的,可以通过前缀和+二分预处理出以每个点为起点到的终点和装了多少个土豆。有个小技巧,将数组扩大2倍来模拟环。然后可以发现每个点最多只有一次做为起点,可以去 \(O(n)\) 的去找循环节。注意有可能一开始不是循环的,可能是到某个点之后开始循环,这时候 \(K\) 要减掉不是循环的部分。

代码

#include <bits/stdc++.h>
#define int long long
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int N = 4e5 + 10;
int a[N], sum[N];
PII vis[N];
bool vis2[N];

void solve() {
    int n, q, x;
    cin >> n >> q >> x;
    for (int i = 1; i <= n; i++) cin >> a[i];
    for (int i = n + 1; i <= 2 * n; i++) {
        a[i] = a[i - n];
    }
    for (int i = 1; i <= 2 * n; i++) {
        sum[i] = sum[i - 1] + a[i];
    }
    for (int i = 1; i <= n; i++) {
        int tmp = sum[n + i - 1] - sum[i - 1];
        int y = x % tmp, s = x / tmp;
        if (!y) {
            vis[i].first = i - 1;
            vis[i].second = s * n;
            continue;
        }
        int l = i, r = n + i - 1;
        while (l < r) {
            int mid = l + r >> 1;
            if (sum[mid] - sum[i - 1] + tmp * s >= x) r = mid;
            else l = mid + 1;
        }
        vis[i].first = (l > n ? l - n : l);
        vis[i].second = l - i + 1 + s * n;
    }
    int st = 1;
    vector<int> v, v1, v2;
    while (!vis2[st]) {
        v.push_back(st);
        vis2[st] = 1;
        st = vis[st].first + 1;
        if (st == n + 1) st = 1;
    }
    int id;
    for (int i = 0; i < v.size(); i++) {
        if (v[i] != st) v1.push_back(v[i]);
        else {
            id = i;
            break;
        }
    }
    for (int i = id; i < v.size(); i++) {
        v2.push_back(v[i]);
    }
    int len = v1.size(), len2 = v2.size();
    while(q--) {
        int k;
        cin >> k;
        k--;
        if (k < len) {
            cout << vis[v1[k]].second << "\n";
        } else {
            k -= len;
            int y = k % len2;
            cout << vis[v2[y]].second << "\n";
        }
    }
}

G - Triangle

给定一个 \(N\) 个点的无向图,形式是 \(N \times N\) 的一个矩阵 \(A\)\(A_{i j}\) 如果为1 说明 \(i\)\(j\) 有一条边,否则没边。求三元环的个数。 \(1\le N \le 3000\)

用 bitset 枚举两个点,这两个点能到的点的集合相与 1的个数就是对答案的贡献,时间复杂度 \(O(n^3 / 64)\)

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 3010;
bitset<3000> g[N];

void solve() {
    int n;
    cin >> n;
    for (int i = 0; i < n; i++) {
        string s;
        cin >> s;
        for (int j = i; j < n; j++) {
            if (s[j] == '1') g[i][j] = 1;
        }
    }
    ll ans = 0;
    for (int i = 0; i < n; i++) {
        for (int j = i + 1; j < n; j++) {
            if (g[i][j]) {
                ans += (g[i] & g[j]).count();
            }
        }
    }
    cout << ans << "\n";
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    int t = 1;
    while (t--) {
        solve();
    }
    return 0;
}
posted @ 2022-08-11 23:43  回忆垃圾桶  阅读(33)  评论(0)    收藏  举报