排位赛 12 题解

排位赛 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;
}

你可能感兴趣的:(排位赛 12 题解)