Loading

20230413 训练记录:最小生成树

删除一段使得剩下数是 15 倍数的方案数

注意到 \(15n\) 必定以 \(0/5\) 结尾,因此有两种情况:

\(n\) 的数位和为 \(s\)

  1. 末尾是 \(0/5\),要删除的是中间某一段和为 \(s \bmod 3\) 的一段。
  2. 否则删除的是后缀,遇到每一个 \(0/5\) 时,判断前缀是否是 \(3\) 的倍数。
没 OJ 就在这里交一下好啦
#include <bits/stdc++.h>

using ll = long long;

int main() {
    std::cin.tie(nullptr)->sync_with_stdio(false);
    
    std::string s;
    std::cin >> s;
    
    int n = s.size();
    
    ll sum = 0;
    std::vector<int> a(n);
    for (int i = 0; i < n; i++) {
        a[i] = s[i] - '0';
        sum += a[i];
    }
    
    assert(n == 1 || a[0] != 0);

    ll ans = 0;
    if (int last = a.back(); last == 0 || last == 5) {
        int need = (3 - last % 3) % 3;
        std::unordered_map<int, int> counter{};
        for (int i = 0, prefix = 0; i < n - 1; i++) {
            ans += counter[need];
            prefix = (prefix + a[i]) % 3;
            counter[prefix] += 1;
        }
    } else {
        for (int i = 0, prefix = 0; i < n; i++) {
            prefix = (prefix + a[i]) % 3;
            if (a[i] == 0 && prefix == 0) {
                ans += 1;
            } else if (a[i] == 5 && prefix == 1) {
                ans += 1;
            }
        }
    }
    
    std::cout << ans << '\n';
    
    return 0;
}

公路修建 / Prim 模板题

完全没学过 Prim,但是 OIwiki 上毛估估看一眼大概是贪心加最近的点,复杂度是 \(\mathcal O(n^2)\)

展开代码
#include <bits/stdc++.h>

using ll = long long;
using db = double;

const int N = 5010;
int n, vis[N];
ll dis[N];

struct point {
    ll x, y;
    ll dist(const point &_) const {
        return (x - _.x) * (x - _.x) +  (y - _.y) * (y - _.y);
    }
} points[N];

void prim() {
    memset(dis, 0x3f, sizeof dis);
    
    dis[1] = 0;
    for (int i = 1; i <= n - 1; i++) {
        int x = 0;
        for (int j = 1; j <= n; j++) {
            if (!vis[j] && (!x || dis[j] < dis[x])) {
                x = j;
            }
        }
        vis[x] = true;
        for (int j = 1; j <= n; j++) {
            if (!vis[j]) {
                ll nd = points[x].dist(points[j]);
                dis[j] = std::min(dis[j], nd);
            }
        }
    }
}

int main() {
    scanf("%d", &n);
    for (int i = 1; i <= n; i++) {
        scanf("%lld%lld", &points[i].x, &points[i].y);
    }
    
    prim();
    
    double ans = 0;
    for (int i = 1; i <= n; i++) {
        ans += std::sqrt(1. * dis[i]);
    }
    printf("%.2lf\n", ans);
    
    return 0;
}

P2573 [SCOI2012] 滑雪

\(n\) 个点 \(m\) 条边,点权表示点的高度,边权表示距离。只能从高处向低(非严格)处 “滑行”,可以传送到曾经走过的点。问最多能去多少点以及最小 “滑行” 距离和。

第一问容易,主要是第二问。

大一的时候就听过这个 “回退” 的说法:2021牛客寒假算法基础集训营 6。即走过的连通块再访问无花费,要最小花费的话就是最小生成树。
但另一个限制,只能从高向低处走该怎么解决?按照高度从大到小排就可以啦~ 这样就能保证在不使用传送的情况下一条路径上的点权是不上升的。

展开代码
#include <bits/stdc++.h>

using ll = long long;

const int N = 100010, M = 2000010;

int n, m, cnt, _cnt, h[N], height[N];
ll sum;

struct edge {
    int u, v, w, t;
    bool operator< (const edge &_) const {
        return height[v] != height[_.v] ? height[v] > height[_.v] : w < _.w;
    }
} edges[M];

void link(int u, int v, int w) {
    edges[++_cnt] = { u, v, w, h[u] }, h[u] = _cnt;
}

int vis[N];
void dfs(int u) {
    if (vis[u]) return;
    vis[u] = 1;
    for (int i = h[u]; i; i = edges[i].t) {
        dfs(edges[i].v);
    }
}

int p[N];
int find(int x) {
    return p[x] = p[x] == x ? x : find(p[x]);
}

int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; i++) {
        scanf("%d", height + i);
    }
    for (int i = 1, u, v, w; i <= m; i++) {
        scanf("%d%d%d", &u, &v, &w);
        if (height[u] >= height[v]) link(u, v, w);
        if (height[v] >= height[u]) link(v, u, w);
    }
    
    dfs(1);
    
    int ans = std::count(vis + 1, vis + n + 1, 1);
    printf("%d ", ans);
    
    std::iota(p, p + n + 1, 0);
    std::sort(edges + 1, edges + 1 + _cnt);
    for (int i = 1; i <= _cnt; i++) {
        int u = edges[i].u, v = edges[i].v, w = edges[i].w;
        if (vis[u] && vis[v]) {
            int fu = find(u), fv = find(v);
            if (fu != fv) {
                sum += w;
                p[fv] = fu;
                cnt += 1;
            }
            if (cnt == ans - 1) break;
        }
    }
    
    printf("%lld\n", sum);
    
    return 0;
}

今天好怠惰啊。。。

posted @ 2023-04-14 01:29  PatrickyTau  阅读(12)  评论(0编辑  收藏  举报