ch12参考思路

发布于:2025-07-20 ⋅ 阅读:(16) ⋅ 点赞:(0)

F. 省钱

建图后跑最小生成树即可。建图:

  • 按 x 排序,相邻的点之间连边
  • 按 y 排序,相邻的点之间连边
#include <bits/stdc++.h>
using namespace std;

const int maxn = 100010, maxm = 200010;
struct edge {
    int u, v, w;
    bool operator<(const edge &n) const { return w < n.w; }
}E[maxm];
int fa[maxn], n, m;
void init() {
    for (int i = 1; i <= n; i++) fa[i] = i;
}
int find(int x) {
    if (fa[x] == x) return x;
    return fa[x] = find(fa[x]);
}
void uni(int x, int y) {
    int rx = find(x), ry = find(y);
    fa[ry] = rx;
}
long long kru() {
    init();
	sort(E, E + m);
    long long ans = 0;
    for (int i = 0; i < m; ++i) {
        edge e = E[i];
        if (find(e.u) != find(e.v)) {
			uni(e.u, e.v);
			ans += e.w;
		}
    }
    return ans;
}
// ----------------- 最小生成树模板

struct Node{
    int id, x, y;
}node[maxn];
bool cmpX(Node x, Node y) {
    return x.x < y.x;
}
bool cmpY(Node x, Node y) {
    return x.y < y.y;
}
// ------------------ 按 X Y 排序类似 ch01 的 A 题 
int main() {
    cin >> n;
    for (int i = 1; i <= n; ++i) {
        cin >> node[i].x >> node[i].y;
        node[i].id = i;
    }
    sort(node + 1, node + 1 + n, cmpX);
    for (int i = 2; i <= n; ++i) {
        E[m].u = node[i - 1].id, E[m].v = node[i].id;
        E[m].w = node[i].x - node[i - 1].x;
        ++m;
    }
    sort(node + 1, node + 1 + n, cmpY);
    for (int i = 2; i <= n; ++i) {
        E[m].u = node[i - 1].id, E[m].v = node[i].id;
        E[m].w = node[i].y - node[i - 1].y;
        ++m;
    }
    cout << kru() << "\n";
    return 0;
}

G. 打包

作为附加题。

对每个起点使用二分找终点。

每包确定起点,终点就一定固定,只有 n 对起点和终点,则 n+1 次打包中,一定有循环节。

将循环开始的盒数和循环节长度记录下来,就可以做到 O(1) 回答询问

参考代码:

#include <bits/stdc++.h>
using namespace std;

const int maxn = 200010;
using ll = long long;
bool vis[maxn];
ll sum[maxn], x;
ll mp[maxn], mp2[maxn];
int n;
// 当前盒子从数组下标 _p 开始,返回下一个盒子开始的数组下标(先不取模,因为要统计盒子中的蛋糕个数)
ll find(ll _p) { // find nxt  
	ll p = _p % n, s = x, cnt = 0;
	if (p == 0) p = n;
	cnt += s / sum[n] * n, s %= sum[n];
	if (s > sum[n] - sum[p - 1]) {
		cnt += n - p + 1, s -= sum[n] - sum[p - 1];
		p = 1;
	}  
	int pos = lower_bound(sum + 1, sum + 1 + n, sum[p - 1] + s) - sum;
	cnt += pos - p + 1;	
	return _p + cnt;
}
// 第 1 个盒子从数组下标 p = 1 开始使用重量 w[1]
ll p = 1, cnt = 0;
void init() {
	while (true) {
		if (vis[p % n]) return;
		vis[p % n] = 1;
		ll nxt = find(p);
        // 存第 cnt 个盒子装的蛋糕个数
		mp[++cnt] = nxt - p;
        // 记录下标 p 在路径中是第几个
		mp2[p % n] = cnt, p = nxt;
	}
}
int main() {
	int q;
	ll k, a;
	cin >> n >> q >> x;
	for (int i = 1; i <= n; ++i) {
		cin >> a;
		sum[i] = sum[i - 1] + a;
	}
	init();
    // st: 环中的起点,cir: 环的大小
	ll st = mp2[p % n], cir = cnt + 1 - st;
	while (q--) {
		cin >> k;
		if (k < st) cout << mp[k] << "\n"; // 在环前面的链中
		else cout << (mp[st + (k - st) % cir]) << "\n"; // 在环中
	}
	return 0;
}

I. 修炼

枚举每个点作为起点 O(n),二分 S O(log 4e9)。已知起点和 S 后暴力建图跑 dfs/bfs O(n(n+m))

#include <bits/stdc++.h>
using namespace std;

using ll = long long;
int n, x[210], y[210], p[210];
bool vis[210];
void dfs(int u, ll s) {
    vis[u] = 1;
    for (int v = 1; v <= n; ++v) {
        if (!vis[v] && p[u] * s - abs(x[u] - x[v]) - abs(y[u] - y[v]) >= 0)
            dfs(v, s);
    }
}
bool check(int st, ll s) {
    memset(vis, 0, sizeof(vis));
	dfs(st, s);
    for (int i = 1; i <= n; ++i) {
        if (!vis[i]) return 0;
    }
    return 1;
}
int main() {
    cin >> n;
    for (int i = 1; i <= n; ++i) {
        cin >> x[i] >> y[i] >> p[i];
    }
	ll res = 4e9;
	for (int i = 1; i <= n; ++i) {
		ll l = 0, r = 4e9;
		while (l <= r) {
			ll mid = (l + r) / 2;
			if (check(i, mid)) r = mid - 1, res = min(res, mid);
			else l = mid + 1;
		}
	}
	cout << res << endl;
    return 0;
}

网站公告

今日签到

点亮在社区的每一天
去签到