【JZOJ比赛】2019.11.9 JZOJ B组

1402 偷懒的小X

题意

给出n个数,把它们放进小根堆里,求出最大的字典序。

思路

考虑将数从小到大放入,那么从父节点到子节点放可保证小根堆的性质。
为了保证字典序最大,从右节点开始放(为了让大的数在左节点),递归填数即可。

代码

#include <cstdio>
#include <iostream>
#include <algorithm>

int n, pos;
int a[70001], b[70001];

void dfs(int p, int dep) {
	if (p * 2 + 1 <= n && !b[p * 2 + 1]) {
		b[p * 2 + 1] = a[++pos];
		dfs(p * 2 + 1, pos);
	}
	if (p * 2 <= n && !b[p * 2]) {
		b[p * 2] = a[++pos];
		dfs(p * 2, pos);
	}
}

int main() {
	scanf("%d", &n);
	for (int i = 1; i <= n; i++)
		scanf("%d", &a[i]);
	std::sort(a + 1, a + n + 1);
	b[pos = 1] = a[1];
	dfs(1, 1);
	for (int i = 1; i <= n; i++)
		printf("%d ", b[i]);
}

1403 渡河

题意

给出一个值为(0~1)的矩阵,从0到1的代价为1,求出每个询问的点走到边界的最小代价。

思路

考虑从边界走到询问点,那么用spfa求解最短路即可,注意判断边界上值为1的情况。

代码

#include <queue>
#include <cstdio>
#include <cstring>

const int dx[] = {1, 1, 1, 0, 0, -1, -1, -1}, dy[] = {1, 0, -1, 1, -1, 1, 0, -1};
struct node {
	int x, y, w;
};
int n, q, tot;
int a[1001][1001];
int d[1001][1001], v[1001][1001];
char c[1001];

bool operator <(const node &a, const node &b) {
	return a.w > b.w;
}

inline int input() {
	int res = 0;
	char c = getchar();
	while (c > '9' || c < '0') c = getchar();
	while (c >= '0' && c <= '9') res = (res << 3) + (res << 1) + c - 48, c = getchar();
	return res;
}

inline void spfa() {
	std::queue<node> q;
	memset(d, 127 / 3, sizeof(d));
	for (register int i = 1; i <= n; i++) {
		q.push((node){1, i, d[1][i] = a[1][i]});
		q.push((node){n, i, d[n][i] = a[n][i]});
		if (i > 1 && i < n) q.push((node){i, 1, d[i][1] = a[i][1]}), q.push((node){i, n, d[i][n] = a[i][n]});
	}
	while (q.size()) {
		int x = q.front().x, y = q.front().y;
		q.pop();
		v[x][y] = 0;
		for (register int i = 0, verx, very; i < 8; i++) {
			verx = x + dx[i];
			very = y + dy[i];
			if (verx < 1 || verx > n || very < 1 || very > n) continue;
			if (d[verx][very] > d[x][y] + (!a[x][y] && a[verx][very])) {
				d[verx][very] = d[x][y] + (!a[x][y] && a[verx][very]);
				if (!v[verx][very]) {
					q.push((node){verx, very, d[verx][very]});
					v[verx][very] = 1;
				}
			}
		}
	}
}

signed main() {
	n = input();
	q = input();
	for (register int i = 1; i <= n; i++) {
		scanf("%s", c);
		for (register int j = 1; j <= n; j++)
			a[i][j] = c[j - 1] - 48;
	}
	spfa();
	for (register int x, y; q; q--) {
		x = input();
		y = input();
		printf("%d ", d[x][y]);
	}
}

1404 菱形内的计数

毒瘤题,未做。


1405 电缆建设

题意

求MST,点的横坐标固定。

思路

由于数据很大,所以普通做法不可取,考虑题目给出的情况。

横坐标固定,故点与点不需要都连边,可发现一个点只用和与它最近的两点以及同一排的两点连边即可。

代码

#include <cmath>
#include <cstdio>
#include <algorithm>

struct node {
	int u, v;
	double w;
}e[2400001];
int n, m, x1, x2, cnt;
int fa[1200001], p1[600001], p2[600001];
double ans;

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

double dis(int a, int b) {
	return sqrt((long long)(p1[a] - p2[b]) * (p1[a] - p2[b]) + (long long)(x1 - x2) * (x1 - x2));
}

bool operator <(const node &x, const node &y) {
	return x.w < y.w;
}

int main() {
	scanf("%d %d %d %d", &n, &m, &x1, &x2);
	for (int i = 1, x; i <= n; i++) {
		scanf("%d", &x);
		p1[i] = p1[i - 1] + x;
		if (i > 1) e[++cnt].u = i - 1, e[cnt].v = i, e[cnt].w = x;
		fa[i] = i;
	}
	for (int i = 1, x; i <= m; i++) {
		scanf("%d", &x);
		p2[i] = p2[i - 1] + x;
		if (i > 1) e[++cnt].u = i - 1 + n, e[cnt].v = i + n, e[cnt].w = x;
		fa[i + n] = i + n;
	}
	
	for (int i = 1, j = 1; i <= n; i++) {
		for (; j < m && p1[i] > p2[j]; j++);
		e[++cnt].u = i;
		e[cnt].v = j + n;
		e[cnt].w = dis(i, j);
		if (j > 1) {
			e[++cnt].u = i;
			e[cnt].v = j + n - 1;
			e[cnt].w = dis(i, j - 1);
		}
	}
	std::sort(e + 1, e + cnt + 1);
	for (int i = 1; i <= cnt; i++) {
		int f1 = find(e[i].u), f2 = find(e[i].v);
		if (f1 == f2) continue;
		fa[f1] = f2;
		ans += e[i].w;
	}
	printf("%.2f", ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值