「考试报告」2023.5.21 模拟赛

发布时间 2023-05-21 15:46:14作者: yi_fan0305

earth

【题目描述】

“啊,地球,我的流浪地球……”
——《流浪地球》
在一条直线上,从左到右排列着 \(n\) 台地球发动机,每台发动机有着固定的位置坐标 \(A_i\) 和功率 \(P_i\),保证 \(A_i<A_i+1\)。此外,由于地球发动机的特性,每台发动机还有一个参数 \(X_i\),如果一台发动机运行,则坐标范围在 \(\left[A_i,A_i+X_i \right]\) 的其它发动机就无法运行。现在你想让正在运行的发动机总功率最大,请输出这个总功率。

【输入数据】

第一行一个整数 \(n\),意义如上所述。
接下来 \(n\) 行,每行三个整数 \(A_i,P_i,X_i\),意义如题面所述。

【输出数据】

一行一个整数,表示可能的最大功率。

【样例输入】

4
2 5 1
5 4 3
8 10 3
9 2 2

【样例输出】

15

【数据范围】

对于 \(20\%\) 的数据,\(n \le 10,0 < A_i,P_i,X_i \le 10\)
对于 \(50\%\) 的数据,\(n \le 2000,0 < A_i,P_i,X_i \le 10^5\)
对于 \(100\%\) 的数据,\(n \le 105,0 < A_i,P_i,X_i \le 10^9\)


一个 DP 题,调试时多写了个括号忘删了,然后第一个点就被卡了。
\(dp_i\):第 \(i\) 个发动机能达到的最大总功率。
\(dp_i = \max(dp_{i + 1}, dp_x + p_i)\)\(x\) 为距离 \(> A_i + x_i\) 的最小位置,可以二分找到。

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

const int N = 1e5 + 5;

int n;
ll A[N], p[N], x[N], dp[N];

int main() {
	scanf("%d", &n);
	for (int i = 1; i <= n; ++ i) {
		scanf("%lld%lld%lld", &A[i], &p[i], &x[i]);
	}
	dp[n] = p[n];
	for (int i = n - 1; i >= 1; -- i) {
		int pos = A[i] + x[i];
		if (A[i + 1] > pos) {
			dp[i] = dp[i + 1] + p[i];
		}
		else {
			if (A[n] < pos) {
				dp[i] = max(dp[i + 1], p[i]);
			}
			else {
				int x = upper_bound(A + i, A + n + 1, pos) - A;
				dp[i] = max(dp[i + 1], dp[x] + p[i]);
			}
		}
	}
	printf("%lld\n", dp[1]);
	return 0;
}

graph

【题目描述】

\(H\) 有一张 \(n\) 个点,\(m\) 条边的无向连通图,他想从图中选出一些边,保证通过这些边 \(a\)\(b\) 连通,\(c\)\(d\) 连通,同时选出的边数尽量少。

【输入数据】

第一行两个整数 \(n,m\),表示图中的边数和点数。
第二行四个整数 \(a,b,c,d\),意义如题面所述。
接下来 \(m\) 行,每行两个整数 \(a,b\),表示点 \(a\) 和点 \(b\) 间有一条边。

【输出数据】

一行一个整数,表示最少需要选出的边数。

【样例输入】

5 8
3 4 1 3
2 1
3 2
4 3
5 3
4 2
1 4
5 4
2 1

【样例输出】

2

【数据范围】

对于所有数据,保证 \(1 \le a,b,c,d \le n\)
对于 \(10\%\) 的数据,\(0 < n,m \le 20\);
对于 \(30\%\) 的数据,\(0 < n,m \le 300\);
对于 \(60\%\) 的数据,\(0 < n \le 300\);
对于 \(100\%\) 的数据,\(0 < n,m \le 3000\)


如果路径不相交,则答案为 \(a \Rightarrow b\)\(c \Rightarrow d\) 的最短路,如果有相交的部分, \(n^2\) 枚举 相交部分的起始点和终点,判断这一段是否相交,求最大值。
点与点之间的最小距离,由于没有边权,可以直接设为 \(1\),然后用 bfs 来做,\(O_{n^2}\)堆优化的 dijkstra 也跑不过,别问我是怎么知道的。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll, int> pli;

const int N = 3010;

int n, m, a, b, c, d;
ll dis[N][N];
bool vis[N], e[N][N];
vector<int> son[N];

void bfs(int s) {
	for (int i = 1; i <= n; ++ i) {
		vis[i] = 0;
	}
	queue<int> q;
	q.push(s);
	vis[s] = 1;
	while (!q.empty()) {
		int u = q.front();
		q.pop();
		for (int v : son[u]) {
			if (vis[v])	continue;
			dis[s][v] = dis[s][u] + 1;
			vis[v] = 1;
			q.push(v);
		}
	}
}

int main() {
	memset(dis, 0x3f, sizeof dis);
	scanf("%d%d", &n, &m);
	scanf("%d%d%d%d", &a, &b, &c, &d);
	for (int i = 1; i <= n; ++ i) {
		dis[i][i] = 0;
	}
	for (int i = 1, x, y; i <= m; ++ i) {
		scanf("%d%d", &x, &y);
		if (e[x][y])	continue;
		e[x][y] = e[y][x] = 1;
		son[x].push_back(y);
		son[y].push_back(x);
	}
	for (int i = 1; i <= n; ++ i) {
		bfs(i);
	}
	ll maxx = 0;
	for (int x = 1; x <= n; ++ x) {
		for (int y = 1; y <= n; ++ y) {
			if (x == y)	continue;
			if ((dis[a][x] + dis[x][y] + dis[y][b] == dis[a][b] || 
				dis[a][y] + dis[x][y] + dis[x][b] == dis[a][b]) &&
				(dis[c][x] + dis[x][y] + dis[y][d] == dis[c][d] || 
					dis[c][y] + dis[x][y] + dis[x][d] == dis[c][d])) {
				maxx = max(maxx, dis[x][y]);
			}
		}
	}
	printf("%lld\n", dis[a][b] + dis[c][d] - maxx);
	fclose(stdin);
	fclose(stdout);
	return 0;
}

tree

【题目描述】

\(H\) 有一棵 \(n\) 个节点的树 \(T\),每个节点上有一个非负整数 \(A_i\),他想知道所有距离不超过k的点对 \(\left(x , y\right) (x<y)\) 上数的亦或值的和。

【输入数据】

第一行两个正整数 \(n,k\),表示树上的节点数和给定的距离 \(k\)
第二行 \(n\) 个非负整数,第 \(i\) 个数表示第 \(i\) 个节点上的数是 \(A_i\)
接下来 \(n-1\) 行,每行两个正整数 \(u,v\),表示节点 \(u\) 和节点 \(v\) 之间有一条边。保证输入是一棵树。

【输出数据】

一行一个整数,表示所有距离不超过k的点对上数的亦或值的和。

【样例输入】

6 3
7 4 4 3 2 0 
2 1
6 2
5 6
3 5
4 1

【样例输出】

51

【数据范围】

测试点编号 n k \(A_i\) 特殊性质
1 300 n 220
2 300 n 220
3 300 n 220
4 2000 n 2
5 2000 n 28
6 2000 20 220
7 2000 n 220 树是一条链
8 2000 n 220
9 105 n 2
10 105 n 28
11 105 20 220
12 105 n 220 树是一条链
13 105 n 220
14 105 n 220
15 3*105 n 2
16 3*105 n 28
17 3*105 20 220
18 5*105 n 220 树是一条链
19 5*105 n 220
20 5*105 n 220

40分的暴力很好打,\(n^2\) 暴力。
正解:点分治
暴力:

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

const int N = 2010;

int n, k;
int dep[N];
ll a[N], dis[N][N];
vector<int> son[N];

void dfs(int u, int fat) {
	for (int v : son[u]) {
		if (v == fat)	continue;
		dep[v] = dep[u] + 1;
		dfs(v, u);
	}
}

int main() {
	freopen("tree.in", "r", stdin);
	freopen("tree.out", "w", stdout);
	scanf("%d%d", &n, &k);
	if (n > 2000) {
		cout << rand();
		fclose(stdin);
		fclose(stdout);
		return 0;
	}
	for (int i = 1; i <= n; ++ i) {
		scanf("%lld", &a[i]);
	}
	for (int i = 1, x, y; i <= n - 1; ++ i) {
		scanf("%d%d", &x, &y);
		son[x].push_back(y);
		son[y].push_back(x);
	}
	for (int i = 1; i <= n; ++ i) {
		for (int j = 1; j <= n; ++ j) {
			dep[j] = 0;
		}
		dfs(i, 0);
		for (int j = 1; j <= n; ++ j) {
			dis[i][j] = dep[j];
		}
	}
	ll ans = 0;
	for (int x = 1; x < n; ++ x) {
		for (int y = x + 1; y <= n; ++ y) {
			if (dis[x][y] <= k) {
				ans += (a[x] ^ a[y]);
			}
		}
	}
	printf("%lld\n", ans);
	fclose(stdin);
	fclose(stdout);
	return 0;
}