排位赛 12 题解
A - Cards
题意
给出n
个数字,求如何两两分组可以让每组数字之和相等。
思路
水。暴力。
代码
#include
using namespace std;
#define LOCAL
int main(){
#ifdef LOCAL
freopen("in.txt", "r", stdin);
#endif
int n; scanf("%d", &n);
int num[107]; int sum = 0;
for (int i = 0; i < n; ++i) {
scanf("%d", num+i);
sum += num[i];
}
int par = sum/(n/2);
int vis[107] = {};
for (int i = 0; i < n; ++i) {
if (vis[i]) continue;
for (int j = i+1; j < n; ++j) {
if (vis[j]) continue;
if (num[i] + num[j] == par) {
vis[j] = 1;
printf("%d %d\n", i+1, j+1);
break;
}
}
}
return 0;
}
B - Cells Not Under Attack
题意
在 $n\times n$ 的棋盘上放车,求棋盘上有多少个点不能被这些车攻击到。
思路
去掉被车覆盖的行和列即可。
代码
#include
using namespace std;
#define LOCAL
#define LL __int64
const int maxn = 1e5+7;
int row[maxn], col[maxn];
int main(){
#ifdef LOCAL
freopen("in.txt", "r", stdin);
#endif
int n, m;
scanf("%d%d", &n, &m);
LL x = n, y = n;
memset(row, 0, sizeof row);
memset(col, 0, sizeof col);
while (m--) {
int a, b; scanf("%d%d", &a, &b);
if (!row[a]) ++row[a], --x;
if (!col[b]) ++col[b], --y;
printf("%I64d ", x*y);
}
return 0;
}
C - They Are Everywhere
题意
给出一个字符串,找出最短的包含所有出现过的字母的子串的长度。
思路
尺取法。用两个变量作为指针,先移动后面的,再移动前面的,更新每个字符出现的次数。维护最小值。
代码
#include
using namespace std;
#define LOCAL
const int maxn = 1e5+7;
int main(){
#ifdef LOCAL
freopen("in.txt", "r", stdin);
#endif
int n; scanf("%d", &n);
char s[maxn]; scanf("%s", s);
map vis;
for (int i = 0; i < n; ++i) vis[s[i]] = 1;
int let_num = vis.size(); vis.clear();
int cnt = 1, l = 0, r = 0;
vis[s[0]] = 1;
int ans = 0x3f3f3f3f;
while (r < n) {
while (cnt < let_num) {
if (++r >= n) break;
if (!vis[s[r]]) ++cnt;
++vis[s[r]];
}
while (cnt >= let_num) {
ans = min(ans, r-l+1);
--vis[s[l]];
if (!vis[s[l++]]) --cnt;
}
}
printf("%d\n", ans);
return 0;
}
D - As Fast As Possible
题意
一群小学生出门,小学生走路速度为v1
,大巴速度为v2
,大巴一次能载人k
个,路痴·求n
个小学生走完l
长度的路的最短时间。所有人都只能最多坐一次大巴。
思路
最短时间就是最后一个人到达的时间,那么我们要让所有人坐大巴的时间相同。设每个人坐大巴的时间为t1
,大巴送过一批人之后回头接到另一批人的间隔时间为t2
,将n
个人分为p
组,我们只要解如下的方程组即可。
$$
\left{
\begin{aligned}
p\cdot t_1+(p-1)t_2 = &t, \
t_1\cdot v_2+(t-t_1)v_1 = &L, \
t_2\cdot v_2+(t_1+t_2)v_1 = &t_1\cdot v_2
\end{aligned}
\right.
$$
代码
#include
using namespace std;
#define LOCAL
int main(){
#ifdef LOCAL
freopen("in.txt", "r", stdin);
#endif
double n, l, v1, v2, k;
scanf("%lf%lf%lf%lf%lf", &n, &l, &v1, &v2, &k);
int p = (int)n%(int)k?n/k+1:n/k;
double t1 = l*p/(v2-v1) + l*(p-1)/(v2+v1);
double t2 = v1*p/(v2-v1) + v1*(p-1)/(v2+v1) + 1;
printf("%.8lf\n", t1/t2);
return 0;
}
E - The Unique MST
题意
给出一张图,求最小生成树是否是唯一的。
思路
次小生成树模板题。
代码
#include
#include
#include
#include
#include
using namespace std;
#define LOCAL
const int inf = 0x3f3f3f3f;
const int maxn = 105;
bool vis[maxn], used[maxn][maxn];
int lowc[maxn], pre[maxn], Max[maxn][maxn];
int prim(int cost[][maxn], int n){
int ans = 0;
memset(Max, 0, sizeof Max);
memset(used, false, sizeof used);
memset(vis, false, sizeof vis);
vis[0] = true;
pre[0] = -1;
for (int i = 1; i < n; ++i){
lowc[i] = cost[0][i];
pre[i] = 0;
}
lowc[0] = 0;
for (int i = 1; i < n; ++i){
int minc = inf;
int p = -1;
for (int j = 0; j < n; ++j) if (!vis[j] && minc > lowc[j]){
minc = lowc[j];
p = j;
}
if (minc == inf) return -1;
ans += minc;
vis[p] = true;
used[p][pre[p]] = used[pre[p]][p] = true;
for (int j = 0; j < n; ++j){
if (vis[j]) Max[j][p] = Max[p][j] = max(Max[j][pre[p]], lowc[p]);
if (!vis[j] && lowc[j] > cost[p][j]){
lowc[j] = cost[p][j];
pre[j] = p;
}
}
}
return ans;
}
int ans;
int check(int cost[][maxn], int n){
int Min = inf;
for (int i = 0; i < n; ++i)
for (int j = i + 1; j < n; ++j)
if (cost[i][j] != inf && !used[i][j])
Min = min(Min, ans + cost[i][j] - Max[i][j]);
if (Min == inf) return -1;
return Min;
}
void solve(){
int cost[maxn][maxn], n, m;
scanf("%d%d", &n, &m);
for (int i = 0; i < n; ++i)
for (int j = 0; j < n; ++j){
if (i == j) cost[i][j] = 0;
else cost[i][j] = inf;
}
while (m--){
int x, y, w;
scanf("%d%d%d", &x, &y, &w);
--x, --y;
cost[x][y] = cost[y][x] = w;
}
ans = prim(cost, n);
if (ans == -1 || ans == check(cost, n)) printf("Not Unique!\n");
else printf("%d\n", ans);
}
int main(){
#ifdef LOCAL
freopen("in.txt", "r", stdin);
#endif
int t; scanf("%d", &t);
while (t--) solve();
return 0;
}
F - Homer Simpson
题意
有t
的时间,有两种汉堡可以吃,
第一种花费m
的时间,第二种花费n
,
要求尽量使得所有时间都被利用起来。
思路
完全背包问题。
代码
#include
#include
#include
using namespace std;
#define LOCAL
const int maxn = 1e5+7;
const int inf = 0x3f3f3f3f;
int dp[maxn];
int main(){
#ifdef LOCAL
freopen("in.txt", "r", stdin);
#endif
int m, n, t;
while (~scanf("%d%d%d", &m, &n, &t)) {
for (int i = 1; i <= t; ++i) dp[i] = -inf;
dp[0] = 0;
for (int i = m; i <= t; ++i)
dp[i] = max(dp[i], dp[i-m]+1);
for (int i = n; i <= t; ++i)
dp[i] = max(dp[i], dp[i-n]+1);
int ans = t;
while (dp[ans] < 0) --ans;
printf("%d", dp[ans]);
ans == t? puts("") : printf(" %d\n", t-ans);
}
return 0;
}
G - Pasha and Stick
题意
有一根长为t
的木条,求有多少种方法能将其切成非正方形的矩形。
思路
水。注意点是要判断0。
代码
#include
using namespace std;
#define LOCAL
int main(){
#ifdef LOCAL
freopen("in.txt", "r", stdin);
#endif
int n; scanf("%d", &n);
int half = n/2;
if (n&1)
puts("0");
else if (half&1)
printf("%d", half/2);
else
printf("%d %d", (half-1)/2, half/2-1);
return 0;
}
H - Vika and Squares
题意
有n
种涂料,给出每种涂料的数量。用涂料涂色时,若x
用第i
种涂料,则x+1
用第i+1
种涂料。问最多能连续涂多少块。
思路
模拟。先找出最多能循环涂多少次,然后找出最长的一个字段,和首尾比较,得到结果。注意答案需要用__int64
。
代码
#include
using namespace std;
#define LOCAL
#define LL __int64
const int maxn = 2e5+7;
int main(){
#ifdef LOCAL
freopen("in.txt", "r", stdin);
#endif
int n; scanf("%d", &n);
int num[maxn], mi;
for (int i = 1; i <= n; ++i) {
scanf("%d", num+i);
mi = min(mi, num[i]);
}
LL ans = 0;
ans += 1LL*mi*n;
int flag = 0, t = 0;
// flag -> 子串长度
// t -> 最大子串长度
for (int i = 1; i <= n; ++i) {
num[i] -= mi;
if (!num[i]) {
if (flag) t = max(t, flag);
flag = 0;
}
else ++flag;
}
if (flag) t = max(t, flag);
int ht = 0, i = 1; // head & tail
while(num[i] && i <= n) ++i, ++ht;
i = n;
while(num[i] && i > 0) --i, ++ht;
t = max(t, ht);
printf("%d\n", ans + t);
return 0;
}