首页 > 其他分享 >The 2024 ICPC Asia East Continent Online Contest (II)

The 2024 ICPC Asia East Continent Online Contest (II)

时间:2024-10-01 16:45:55浏览次数:6  
标签:i64 frac Contest int return Asia II using mint

A. Gambling on Choosing Regionals

最差情况就是,强队都和你去一起。因此赛站越小,排名也一定越小。

然后只要动态实现出每个学校最强的若干只队伍就好了。

#include <bits/stdc++.h>

using namespace std;

using i32 = int32_t;
using i64 = long long;

#define int i64

using vi = vector<int>;
using pii = pair<int, int>;

const int inf = INT_MAX / 2;

#define lowbit(x) (x & -x)

struct BinaryIndexedTree {
    int n, sum;
    vi b;

    BinaryIndexedTree(int n) : n(n), b(n + 1) {
        sum = 0;
    }

    void modify(int i, int y) {
        sum += y;
        for (; i <= n; i += lowbit(i))
            b[i] += y;
        return;
    }

    int calc(int i) {
        int ans = 0;
        for (; i; i -= lowbit(i))
            ans += b[i];
        return ans;
    }

    int upper(int x) {
        return sum - calc(x);
    }
};

i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int n, k;
    cin >> n >> k;
    int c = inf;
    for (int i = 1, x; i <= k; i++)
        cin >> x, c = min(c, x);

    vector<pair<int, string>> team(n);
    vi hw;
    for (auto &[w, u]: team)
        cin >> w >> u, hw.push_back(w);

    sort(hw.begin(), hw.end());
    hw.resize(unique(hw.begin(), hw.end()) - hw.begin());

    for (auto &[w, u]: team)
        w = lower_bound(hw.begin(), hw.end(), w) - hw.begin() + 1;

    map<string, vector<pii>> cnt;

    for (int i = 0; i < n; i++)
        cnt[team[i].second].emplace_back(team[i].first, i);

    BinaryIndexedTree bit(hw.size());

    for (auto &[u, t]: cnt) {
        sort(t.begin(), t.end(), greater<>());
        for (int i = 0, N = min(c, (int) t.size()); i < N; i++)
            bit.modify(t[i].first, 1);
    }

    vi res(n);
    for (auto &[u, t]: cnt) {
        for (int i = 0, N = min(c, (int) t.size()); i < N; i++)
            bit.modify(t[i].first, -1);
        for (int i = 0; i < t.size(); i++) {
            res[t[i].second] = bit.upper(t[i].first) + min(i, c - 1) + 1;
        }
        for (int i = 0, N = min(c, (int) t.size()); i < N; i++)
            bit.modify(t[i].first, 1);
    }
    for (int i: res) cout << i << "\n";
    return 0;
}

E. Escape

你和机器人都不能停留在原地。其次\(d\)的限制并不是步数,而是移动半径。

假设你和机器人都可以停留在原地。那么我们可以预处理机器人到达某个点\(x\)的最早时间\(t[x]\)。

如果你到达\(x\)的最早时间为\(f\),则必须要保证\(f < t[x]\)。而对于\(t\),可以直接用多源bfs实现。

我们考虑不能停留的情况,机器人不能提前到达你的路上截停你,而是要在你路径上的某个点反复到达离开。如果\(f<t[x]\)则无所谓。如果\(f>t[x]\),则必须有\((f - t[x])\mod 2 = 0\)。然后要知道,机器人不唯一,到达某个点的路径也不唯一。因此我们要分奇偶求出到达每个点的最早时间。同理,在计算你的到达时间时也要分奇偶考虑。

#include <bits/stdc++.h>

using namespace std;

using i32 = int32_t;
using i64 = long long;

#define int i64

using vi = vector<int>;
using pii = pair<int, int>;

const i32 inf = INT_MAX / 2;

const int mod = 998244353;

void solve() {
    int n, m, d;
    cin >> n >> m >> d;
    vector<vi> e(n + 1);
    for (int i = 1, x, y; i <= m; i++) {
        cin >> x >> y;
        e[x].push_back(y), e[y].push_back(x);
    }

    int k;
    cin >> k;

    vector t(2, vi(n + 1, inf));
    queue<pii> q;

    for (int i = 1, x; i <= k; i++) {
        cin >> x;
        t[0][x] = 0, q.emplace(x, 0);
    }

    vector vis(2, vi(n + 1));
    while (not q.empty()) {
        auto [x, tx] = q.front();
        q.pop();
        if (vis[tx & 1][x]) continue;
        vis[tx & 1][x] = 1;
        if (tx == d) continue;
        for (int ty = tx + 1; auto y: e[x]) {
            if (vis[ty & 1][y]) continue;
            if (t[ty & 1][y] <= ty) continue;
            t[ty & 1][y] = ty, q.emplace(y, ty);
        }
    }

    q.emplace(1, 0);
    vis = vector(2, vi(n + 1));
    vector f(2, vi(n + 1, inf));
    vector lst(2, vi(n + 1, -1));
    f[0][1] = 0;

    while (not q.empty()) {
        auto [x, fx] = q.front();
        q.pop();
        if (vis[fx & 1][x]) continue;
        vis[fx & 1][x] = 1;
        for (int fy = fx + 1; auto y: e[x]) {
            if (vis[fy & 1][y]) continue;
            if (t[fy & 1][y] <= fy) continue;
            if (f[fy & 1][y] <= fy) continue;
            f[fy & 1][y] = fy, lst[fy & 1][y] = x;
            q.emplace(y, fy);
        }
    }

    int ret = min(f[0][n], f[1][n]);
    if (ret == inf) {
        cout << "-1\n";
        return;
    }
    cout << ret << "\n";
    vi p;
    for (int x = n, y = ret & 1; x != -1; x = lst[y][x], y ^= 1)
        p.push_back(x);
    ranges::reverse(p);
    for (auto i: p)
        cout << i << " ";
    cout << "\n";
    return;
}

i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int T;
    cin >> T;
    while (T--)
        solve();
    return 0;
}

F. Tourist

签到

#include <bits/stdc++.h>

using namespace std;


using i32 = int32_t;
using i64 = long long;


i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int n;
    cin >> n;

    i64 cnt = 1500;

    for (int i = 1, x; i <= n; i++) {
        cin >> x, cnt += x;
        if (cnt >= 4000) {
            cout << i << "\n";
            return 0;
        }
    }
    cout << -1;
    return 0;
}

G. Game

平局的状态不影响结果。因此我们可以记Alice 获胜的概率是\(P_a = \frac{a_0}{a_0 + a_1}\),Bob 获胜的概率是\(P_b = \frac{a_1}{a_0 + a_1}\)。

我们可以利用一种类似更相减损术的方法求解。但是复杂度太高了。我们考虑归纳一下。

对于当前的状态\((x,y)\),有以下三种情况

  1. \(x = y\),此时Alice获胜的概率是\(P_a\)
  2. \(x < y\),此时Alice必须连续至少赢得$d = \left \lceil \frac{y - x }{x} \right \rceil $ 局,才有机会获胜,因此可以递归计算\((x , y - dy)\)
  3. \(x>y\),此时Bob必须连续至少赢得$d=\left \lceil \frac{x - y}{y} \right \rceil $ 局,才有机会获胜,因此可以递归计算\((x - dy, y)\)

这样的话,复杂度就和辗转相除法一样是\(O(\log n)\)

#include <bits/stdc++.h>

using namespace std;

using i32 = int32_t;
using i64 = long long;

#define int i64

using vi = vector<int>;
using pii = pair<int, int>;

const i32 inf = INT_MAX / 2;

const int mod = 998244353;

struct mint {
    i64 x;

    mint(int x = 0) : x(x) {}

    int val() const {
        return (x % mod + mod) % mod;
    }

    mint &operator=(int o) { return x = o, *this; }

    mint &operator+=(mint o) { return (x += o.x) >= mod && (x -= mod), *this; }

    mint &operator-=(mint o) { return (x -= o.x) < 0 && (x += mod), *this; }

    mint &operator*=(mint o) { return x = (i64) x * o.x % mod, *this; }

    mint &operator^=(int b) {
        mint w = *this, ret(1);
        for (; b; b >>= 1, w *= w) if (b & 1) ret *= w;
        return x = ret.x, *this;
    }

    mint &operator/=(mint o) { return *this *= (o ^= (mod - 2)); }

    friend mint operator+(mint a, mint b) { return a += b; }

    friend mint operator-(mint a, mint b) { return a -= b; }

    friend mint operator*(mint a, mint b) { return a *= b; }

    friend mint operator/(mint a, mint b) { return a /= b; }

    friend mint operator^(mint a, int b) { return a ^= b; }
};

void solve() {
    int x, y, _;
    cin >> x >> y;
    mint a, b;
    cin >> a.x >> b.x >> _;
    mint pa = a / (a + b), pb = b / (a + b);

    auto calc = [pa, pb](auto &&calc, int x, int y, mint p) -> mint {
        if (x == y) return p * pa;
        if (x < y) {
            int d = (y - x + x - 1) / x;
            return calc(calc, x, y - d * x, p * (pa ^ d));
        } else {
            int d = (x - y + y - 1) / y;
            return p * (1 - (pb ^ d)) + calc(calc, x - d * y, y, p * (pb ^ d));
        }
    };

    cout << calc(calc, x, y, mint(1)).val() << "\n";
}

i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int T;
    cin >> T;
    while (T--)
        solve();
    return 0;
}


I. Strange Binary

\(4\)的倍数无解,然后我们考虑先构造奇数,然后在加1即可

#include <bits/stdc++.h>

using namespace std;

using i32 = int32_t;
using i64 = long long;

#define int i64

using vi = vector<int>;
using pii = pair<int, int>;

const i32 inf = INT_MAX / 2;

const int mod = 998244353;

void solve() {
    int n;
    cin >> n;

    if (n % 4 == 0) {
        cout << "NO\n";
        return;
    }

    bool ok = false;
    if (n % 2 == 0) {
        ok = true;
        n--;
    }

    vi a(32, -1);
    a[31] = 1, n /= 2;
    for (int i = 0; i < 31; i++)
        if (n >> i & 1) a[i] = 1;

    if (ok) a[0] = 0;
    
    cout << "YES";
    for (int i = 0; i < 32; i++)
        cout << " \n"[i % 8 == 0] << a[i];
    cout << "\n";
    return;
}

i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int T;
    cin >> T;
    while (T--)
        solve();
    return 0;
}

J. Stacking of Goods

可以证明按照\(\frac{c_i}{w_i}\)排序一定最优,剩下的贪心就好了。

#include <bits/stdc++.h>

using namespace std;

using i32 = int32_t;
using i64 = long long;

#define int i64

using vi = vector<int>;
using pii = pair<int, int>;

i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);
    int n;
    cin >> n;
    vi w(n), c(n);

    int res = 0;
    for (int i = 0, v; i < n; i++) {
        cin >> w[i] >> v >> c[i];
        res += v;
    }

    vi p(n);
    iota(p.begin(), p.end(), 0);

    sort(p.begin(), p.end(), [&](const int &x, const int &y) -> bool {
        return -c[y] * w[x] > -c[x] * w[y];
    });

    vi W(n);
    for (int i = n - 2; i >= 0; i--)
        W[p[i]] = W[p[i + 1]] + w[p[i + 1]];

    for (auto i: p)
        res -= c[i] * W[i];

    cout << res;
    return 0;
}

L. 502 Bad Gateway

其实可以猜到存在一个阈值\(x\),满足小于等于\(x\)时,不做额外的操作,大于\(x\)时不断的随机。

对于小于等于\(x\)的部分,期望次数就是

\[\frac{1}{T} + \frac{2}{T} + \frac{3}{T}\dots \frac{x}{T} \]

然后我们考虑大于\(x\)的情况,大于的情况总共有\(T - x\)种,每一种的概率都是\(\frac{1}{T}\)。对于随机,随机到小于等于\(x\)的概率为\(\frac {x}{T}\),这就是一个简单的几何分布,因此期望就是\(\frac{T}{x}\)。当我们随机到小于等于\(x\)的情况下,还需要操作的期望是\(\frac{1}{x} + \frac{2}{x} + \frac{3}{x}\dots +\frac{x}{x}\)。因此大于\(x\)部分的期望就是

\[\frac{T-x}{T}(\frac{T}{x} + \frac{1}{x} + \frac{2}{x} + \frac{3}{x}\dots +\frac{x}{x}) \]

因此总的期望此时就是

\[Ex = \frac{x^2(x+1) +(T-x)[2T+x(x+1)]}{2Tx} \]

这个式子很遗憾不满足单调性。但是通过打表发现,当\(T\)取\(1,2,3\dots\)时,\(x\)的取值为

\[1,2,2,3,3,3,4,4,4,4,5,5,5,5,5,\dots \]

#include <bits/stdc++.h>

using namespace std;

using i32 = int32_t;
using i64 = long long;
using i128 = __int128;
#define int i64

using vi = vector<int>;
using pii = pair<int, int>;

const int inf = LLONG_MAX / 2;

using ldb = long double;

const ldb eps = 1e-12;

i128 gcd(i128 a, i128 b) {
    return b ? gcd(b, a % b) : a;
}

ostream &operator<<(ostream &os, i128 x){
    string s;
    while(x > 0) 
        s += char(x % 10 + '0'), x /= 10;
    reverse(s.begin(), s.end());
    os << s;
    return os;
}

struct Z {
    i128 x, y;
    Z() {}

    void norm(){
        i128 d = gcd(x, y);
        x /= d, y /= d; 
    }

    bool operator<(const Z &b) const{
        return x * b.y < b.x * y;
    }

    Z &operator=(Z b) {
        x = b.x, y = b.y;
        return *this;
    }
};


i32 main() {
    ios::sync_with_stdio(false), cin.tie(nullptr);

    int T;
    cin >> T;
    while(T --){
        int n;
        cin >> n;
        auto calc =[&](int x ) -> Z{
            Z ans;
            ans.x = (i128)x * x *(x + 1) + (i128)(n - x) * ((i128)n * 2 + x * (x + 1));
            ans.y = (i128)n * x * 2;
            ans.norm();
            return ans;
        };
        int l = 1 , r = n , ret = -1;
        while(l <= r){
            int mid = (l + r) / 2;
            if(mid * (mid + 1) / 2 >= n) ret = mid, r = mid - 1;
            else l = mid + 1;
        }
        auto res = calc(ret);
        cout << res.x << " " << res.y << "\n";
    }
    return 0;
}

标签:i64,frac,Contest,int,return,Asia,II,using,mint
From: https://www.cnblogs.com/PHarr/p/18442957

相关文章