cf1043 div3 + 2024 ICPC Hangzhou

cf1043 div3 and 2024 ICPC Hangzhou

div3

刚开始没进入状态,写到D还花了一些时间。

D

调了挺久的一道题,边界的处理总是不明不白的,数位DP也设计不明白,还是看题解才勉强过

E

观察到了三分的性质,然后发现三分写不明白,遂抄题解加以体会。

假设只限制取数的总量 \(Z\),且最优的情况是 Vadim 出 \(a\) 张 Kostya 出 \(b\) 张,易得 \(a + b = Z\)。考虑最后一次出牌的情况,此时 Vadim 已经出了 \(a - 1\) 张而 Kostya 已经出了 \(b\) 张。那为什么我们要 Vadim 出而不是 Kostya 出呢,只能是因为 Vadim 的第 \(a\) 大的牌大于 Kostya 的第 \(b + 1\) 大的牌。基于此我们可以发现若 Vadim 出的牌少于 \(a\) 张,则随着 Vadim 出牌数的减少,总和是递减的。同理可以分析出 Vadim 出牌大于 \(a\) 张的情况,总和是随着 Vadim 出牌数的增多而减少的。所以直接三分。

CODE
#include<bits/stdc++.h>

#define IOS std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0)
#ifdef LOCAL_MACHINE
    #define debug(...) printf(__VA_ARGS__)
#else
    #define debug(...)
#endif
using i64 = long long;
using u64 = unsigned long long;
// using i128 = __int128_t;
// using point = std::array<i128, 2>;

constexpr i64 Mod = 998244353;
constexpr int N = 2e5, Inf = 2e9, L = 15;

std::mt19937_64 rnd(std::chrono::steady_clock::now().time_since_epoch().count());

i64 a[N + 5], b[N + 5];

void solve() {
    int n = 0, m = 0, q = 0;
    std::cin >> n >> m >> q;
    for (int i = 1; i <= n; ++i) {
        std::cin >> a[i];
    }
    for (int i = 1; i <= m; ++i) {
        std::cin >> b[i];
    }
    std::sort(a + 1, a + 1 + n, std::greater<i64>());
    std::sort(b + 1, b + 1 + m, std::greater<i64>());

    for (int i = 1; i <= n; ++i) {
        a[i] += a[i - 1];
    }
    for (int j = 1; j <= m; ++j) {
        b[j] += b[j - 1];
    }

    while (q--) {
        int x = 0, y = 0, z = 0;
        std::cin >> x >> y >> z;

        int l = std::max(0, z - y);
        int r = std::min(z, x);

        while (l + 2 < r) {
            int ml = (l + l + r) / 3;
            int mr = (l + r + r) / 3;

            if (a[ml] + b[z - ml] < a[mr] + b[z - mr]) {
                l = ml;
            }
            else {
                r = mr;
            }
        }
        
        i64 ans = 0;
        for (int i = l; i <= r; ++i) {
            ans = std::max(ans, a[i] + b[z - i]);
        }
        std::cout << ans << '\n';
    }
}

int main () {
    IOS;
    int T = 1;
    std::cin >> T;

    while (T--) {
        solve();
    }
    return 0;
}

F

感觉挺一眼的,就是找1到n的路径经过的割边,然后以所有找到的割边两端为起点直接BFS。注意入队的时候编号小的边的端点优先,其他的就正常BFS

因为初始化错了WA一发说是

CODE
#include<bits/stdc++.h>

#define IOS std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0)
#ifdef LOCAL_MACHINE
    #define debug(...) printf(__VA_ARGS__)
#else
    #define debug(...)
#endif
using i64 = long long;
using u64 = unsigned long long;
// using i128 = __int128_t;
// using point = std::array<i128, 2>;

constexpr i64 Mod = 998244353;
constexpr int N = 2e5, Inf = 2e9, L = 15;

std::mt19937_64 rnd(std::chrono::steady_clock::now().time_since_epoch().count());

int n, m;
std::array<int, 2> edge[N + 5];
std::vector<std::pair<int, int>> adj[N + 5];
int near[N + 5];

// 时间戳, 最小值, 时间
int dfn[N + 5], low[N + 5], tim;
bool cut[N + 5];

bool vis[N + 5];

void tarjan(int cur, int fa) {
    dfn[cur] = low[cur] = ++tim;
    for (auto& [to, idx] : adj[cur]) {
        if (to == fa) continue;

        if (dfn[to] == 0) {
            tarjan(to, cur);
            low[cur] = std::min(low[cur], low[to]);
            // 发现儿子和自己都不连通
            if (dfn[cur] < low[to])
                cut[idx] = true;
        }
        else {
            low[cur] = std::min(low[cur], dfn[to]);
        }
    }
}

bool dfs(int cur) {
    vis[cur] = true;
    bool res = false;

    for (auto &[to, idx] : adj[cur]) {
        if (not vis[to]) {
            bool ret = dfs(to);
            if (cut[idx] && !ret) {
                cut[idx] = false;
            }
            res |= ret;
        }
    }
    return res || (cur == n);
}

void init() {
    for (int i = 1; i <= n; ++i) {
        adj[i].clear();
        near[i] = -1;
        dfn[i] = low[i] = 0;
        vis[i] = false;
    }
    for (int i = 1; i <= m; ++i) {
        cut[i] = false;
    }
    tim = 0;
}
void solve() {
    std::cin >> n >> m;
    init();
    for (int i = 1; i <= m; ++i) {
        int u = 0, v = 0;
        std::cin >> u >> v;
        edge[i] = { u, v };
        // std::cin >> edge[i][0] >> edge[i][1];
        adj[u].push_back({ v, i });
        adj[v].push_back({ u, i });
    }
    tarjan(1, 0); // 找出所有割边
    dfs(1); // 排除1到n路径之外的割边

    std::queue<std::pair<int, int>> que;
    for (int i = 1; i <= n; ++i) {
        vis[i] = false;
    }
    for (int i = 1; i <= m; ++i) {
        if (not cut[i]) {
            continue;
        }

        // std::cout << i << '\n';

        auto &[u, v] = edge[i];
        if (not vis[u]) {
            que.push({ u, i });
            vis[u] = true;
        }
        if (not vis[v]) {
            que.push({ v, i });
            vis[v] = true;
        }
        // que.push({ edge[i][0], i });
        // que.push({ edge[i][1], i });
    }

    while (not que.empty()) {
        auto [cur, idx] = que.front();
        que.pop();

        near[cur] = idx;
        for (auto &[to, ind] : adj[cur]) {
            if (not vis[to]) {
                que.push({  to, idx });
                vis[to] = true;
            }
        }
    }

    int q = 0;
    std::cin >> q;
    while (q--) {
        int c = 0;
        std::cin >> c;
        std::cout << near[c] << ' ';
    }
    std::cout << '\n';
}

int main () {
    IOS;
    int T = 1;
    std::cin >> T;

    while (T--) {
        solve();
    }
    return 0;
}

ICPC Hangzhou

ICPC 的题还是困难很多啊

M

关于 GCD 的题常用到差分相关的东西。

题目也就是说对于任意区间都满足 \(gcd(a_l + x, \dots, a_r + x) = min(a[l:r]) + x\),于是我们有 \(gcd(|a_{l + 1} - a_{l}|,\dots , |a_{r} - a_{r - 1}|,min(a[l:r]) + x) = min(a[l:r]) + x\),也就是\(mn = min(a[l:r]) + x\) 可以整除 \(g = gcd(|a_{l + 1} - a_{l}|,\dots , |a_{r} - a_{r - 1}|)\)。于是对于一个特定的区间 \([l, r]\),我们可以枚举 \(g\) 的因数来作为 \(mn + x\) 的值,然后确定 \(x\) 是否是可行的。

现在要明确一点的是对于任意区间的 \(g\)\(g\) 的因数总是待定的 \(mn + x\),且不存在一个不是 \(g\) 的因数的数会成为 \(mn + x\)

所以为了枚举的因数尽可能少,我们取 \([l, r]\) 为整个区间。至于检查只需要在遍历笛卡尔树的过程中判断一下父节点能不能整除所有儿子节点就好了

CODE
#include<bits/stdc++.h>

#define IOS std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0)
#ifdef LOCAL_MACHINE
    #define debug(...) printf(__VA_ARGS__)
#else
    #define debug(...)
#endif
using i64 = long long;
using u64 = unsigned long long;
// using i128 = __int128_t;
// using point = std::array<i128, 2>;

constexpr i64 Mod = 998244353;
constexpr int N = 5e4, Inf = 2e9, K = 50;

std::mt19937_64 rnd(std::chrono::steady_clock::now().time_since_epoch().count());

std::vector<int> prime;
bool vis[N + 5];

int n, k, mn;
int a[N + 5];
std::vector<int> fac;

int stk[N + 5], top, rt;
std::array<int, 2> adj[N + 5];

int cnt;
i64 sum;

// 构建笛卡尔树
void build() {
    for (int i = 0; i < n; ++i) {
        int k = top;
        while (k > 0 && a[stk[k]] > a[i]) {
            k -= 1;
        }
        if (k > 0) {
            adj[stk[k]][1] = i;
        }
        if (k < top) {
            adj[i][0] = stk[k + 1];
        }
        stk[++k] = i;
        top = k;
    }
    rt = stk[1];
}

bool chk(int cur, int x) {
    bool ret = true;
    auto [ls, rs] = adj[cur];
    if (ls != -1) {
        ret &= chk(ls, x);
        ret &= ((a[ls] + x) % (a[cur] + x) == 0);
    }
    if (rs != -1) {
        ret &= chk(rs, x);
        ret &= ((a[rs] + x) % (a[cur] + x) == 0);
    }
    return ret;
}

void dfs(int cur, int val) {
    if (cur == fac.size()) {
        int x = val - mn;
        if (1 <= x && x <= k && chk(rt, x)) {
            cnt += 1;
            sum += (x);
        }
        return;
    }

    int i = cur, j = cur;
    while (j < fac.size() && fac[j] == fac[i]) {
        j += 1;
    }
    for (int t = 0, p = 1; i + t <= j; ++t, p *= fac[i]) {
        dfs(j, val * p);
    }
}

void init(int n = 0) {
    top = 0;
    for (int i = 0; i < n; ++i) {
        adj[i] = { -1, -1 };
    }
}

void solve() {
    std::cin >> n >> k;
    init(n);
    mn = Inf;
    for (int i = 0; i < n; ++i) {
        std::cin >> a[i];
        mn = std::min(mn, a[i]);
    }
    int d = 0;
    for (int i = 1; i < n; ++i) {
        d = std::__gcd(d, std::abs(a[i] - a[i - 1]));
    }

    if (d == 0) {
        std::cout << k << ' ' << 1ll * (1 + k) * k / 2 << '\n';
        return;
    }
    fac.clear();
    for (auto &p : prime) {
        if (p > d) {
            break;
        }

        while (d % p == 0) {
            fac.push_back(p);
            d /= p;
        }
    }
    if (d != 1) {
        fac.push_back(d);
    }

    build();
    cnt = 0;
    sum = 0ll;
    dfs(0, 1);
    std::cout << cnt << ' ' << sum << '\n';
    return;
}

int main () {
    IOS;
    int T = 1;
    std::cin >> T;

    for (int i = 2; i <= N; ++i) {
        if (not vis[i]) {
            prime.push_back(i);
        }

        for (auto &p : prime) {
            if (p * i > N) {
                break;
            }
            vis[p * i] = true;
            if (i % p == 0) {
                break;
            }
        }
    }

    while (T--) {
        solve();
    }
    return 0;
}

F

根据给出的排列建图后,同一个强连通分量中的点一定是相互 fuzzy 的。这时候又有一个非常重要的观察:若将每个数都替换成强连通分量的编号,则题目给出的 \(k\) 个排列都是相等的。然后前缀和处理一下就好了。

CODE
#include<bits/stdc++.h>

#define IOS std::ios::sync_with_stdio(false), std::cin.tie(0), std::cout.tie(0)
#ifdef LOCAL_MACHINE
    #define debug(...) printf(__VA_ARGS__)
#else
    #define debug(...)
#endif
using i64 = long long;
using u64 = unsigned long long;
// using i128 = __int128_t;
// using point = std::array<i128, 2>;

constexpr i64 Mod = 998244353;
constexpr int N = 2e5, Inf = 2e9, L = 15;

std::mt19937_64 rnd(std::chrono::steady_clock::now().time_since_epoch().count());

int a[N + 5], pos[N + 5], lst[N + 5];
i64 pre[N + 5];
std::vector<int> adj[N + 5];

int dfn[N + 5], low[N + 5], tim;
int stk[N + 5], tp;
bool instk[N + 5];
int scc[N + 5], sccid;
void tarjan(int cur) {
    dfn[cur] = low[cur] = ++tim;
    stk[++tp] = cur;
    instk[cur] = true;
    for (auto &to : adj[cur]) {
        if (dfn[to] == 0) {
            tarjan(to);
            low[cur] = std::min(low[cur], low[to]);
        }
        else if (instk[to]) {
            low[cur] = std::min(low[cur], dfn[to]);
        }
    }

    if (dfn[cur] == low[cur]) {
        ++sccid;
        do
        {
            scc[pos[stk[tp]]] = sccid;
            instk[stk[tp]] = false;
        } while (stk[tp--] != cur);
    }
}

i64 S(i64 x) {
    return x * (x - 1) / 2ll;
}

void init(int n = 0) {
    tim = 0;
    tp = 0;
    sccid = 0;
    for (int i = 1; i <= n; ++i) {
        adj[i].clear();
        dfn[i] = low[i] = 0;
        instk[i] = false;
        scc[i] = 0;
    }
}
void solve() {
    int n = 0, k = 0, q = 0;
    std::cin >> n >> k >> q;
    init(n);
    for (int i = 0; i < k; ++i) {
        std::cin >> a[1];
        for (int j = 2; j <= n; ++j) {
            std::cin >> a[j];
            adj[a[j]].push_back(a[j - 1]);
        }
    }

    for (int i = 1; i <= n; ++i) {
        pos[a[i]] = i;
    }
    for (int i = 1; i <= n; ++i) {
        if (scc[i] == 0) {
            tarjan(a[i]);
        }
    }
    // for (int i = 1; i <= n; ++i) {
    //     std::cout << scc[i] << " \n"[i == n];
    // }

    int len = 1;
    for (int i = 1; i <= n; ++i) {
        if (scc[i] != scc[i - 1]) {
            pre[i] = pre[i - 1];
            len = 1;
        }
        else {
            pre[i] = pre[i - 1] - S(len);
            len += 1;
            pre[i] += S(len);
        }
    }
    lst[n] = n;
    for (int i = n - 1; i >= 1; --i) {
        if (scc[i] == scc[i + 1]) {
            lst[i] = lst[i + 1];
        }
        else {
            lst[i] = i;
        }
    }

    i64 ans = 0;
    while (q--) {
        i64 id = 0, l = 0, r = 0;
        std::cin >> id >> l >> r;

        (((id += ans) %= k) += 1);
        (((l += ans) %= n) += 1);
        (((r += ans) %= n) += 1);
        

        if (scc[l] == scc[r]) {
            ans = S(r - l + 1);
        }
        else {
            ans = pre[r] - pre[lst[l]] + S(lst[l] - l + 1);
        }
        std::cout << ans << '\n';
    }
    return;
}

int main () {
    IOS;
    int T = 1;
    std::cin >> T;

    while (T--) {
        solve();
    }
    return 0;
}
posted @ 2025-10-03 21:15  Young_Cloud  阅读(13)  评论(0)    收藏  举报