牛客CSP-S提高组赛前集训营 2 – 解题报告

这次比赛必须要仔细反省,不能再出现暑假的精神状态。

A – 服务器需求

假设我们有很多服务器了,我们现在按天考虑,进行服务器的分配:假设有一个服务器编号集合\(\{id_i\}\),考虑一天天分配若干台,这样就可以保证每一台服务器的分配中不会出现同样的日期。

这样就可以分配完了,直接就有\(\sum a_i \leq |id_i| m\)。然后就是傻逼代码了。

// A.cpp
#include <bits/stdc++.h>
typedef long long ll;
 
using namespace std;
 
const int MAX_N = 4e5 + 200;
 
ll n, m, q, ai[MAX_N];
 
int main()
{
    ll ans = 0;
    multiset<ll> ms;
    scanf("%lld%lld%lld", &n, &m, &q);
    for (int i = 1; i <= n; i++)
        scanf("%lld", &ai[i]), ms.insert(ai[i]), ans += ai[i];
    printf("%lld\n", max(*ms.rbegin(), (ll)(double(ans + m - 1) / double(m))));
    while (q--)
    {
        ll x, y;
        scanf("%lld%lld", &x, &y);
        ans -= ai[x], ms.erase(ms.find(ai[x]));
        ms.insert(ai[x] = y), ans += ai[x];
        printf("%lld\n", max(*ms.rbegin(), (ll)(double(ans + m - 1) / double(m))));
    }
    return 0;
}

B – 沙漠点列

这道题我在考场上只打了 60 分的特判分,还是很好打的。

考虑正解:先求出多少个连通块,然后再处理出有多少个环,来处理\(limit\)极大的情况;当\(limit\)无法直接包揽全部,那么我们优先把所有的割边进行贡献,然后再来破环贡献割边。用 DFS 的方式找出单元环(如果用 Tarjan 直接找就会 GG),然后排序从大到小选即可。

// B.cpp
#include <bits/stdc++.h>

using namespace std;

const int MAX_N = 1e6 + 200, MAX_E = 2e6 + 2000;

int head[MAX_N], current, n, m, limit, ncnt, dfn[MAX_N], low[MAX_N], ptot, stk[MAX_N], tail;
int SCC, aff[MAX_N], dist[MAX_N], loops[MAX_N], loop_cnt;

struct edge
{
    int to, nxt;
} edges[MAX_E << 1];

int read()
{
    char ch = getchar();
    int x = 0, f = 1;
    while (!isdigit(ch))
    {
        if (ch == '-')
            f = -1;
        ch = getchar();
    }
    while (isdigit(ch))
        x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
    return f * x;
}

void addpath(int src, int dst)
{
    edges[current].to = dst, edges[current].nxt = head[src];
    head[src] = current++;
}

void tarjan(int u, int fa)
{
    dfn[u] = low[u] = ++ptot, stk[++tail] = u;
    for (int i = head[u]; i != -1; i = edges[i].nxt)
        if (edges[i].to != fa)
            if (dfn[edges[i].to] == 0)
                tarjan(edges[i].to, u), low[u] = min(low[u], low[edges[i].to]);
            else
                low[u] = min(low[u], dfn[edges[i].to]);
    if (dfn[u] == low[u])
    {
        SCC++;
        do
        {
            aff[stk[tail]] = SCC;
        } while (stk[tail--] != u);
    }
}

void find_loop(int u, int fa)
{
    for (int i = head[u]; i != -1; i = edges[i].nxt)
        if (edges[i].to != fa)
            if (dist[edges[i].to] == 0)
            {
                dist[edges[i].to] = dist[u] + 1;
                find_loop(edges[i].to, u);
            }
            else if (dist[edges[i].to] < dist[u] + 1)
                loops[++loop_cnt] = dist[u] - dist[edges[i].to] + 1;
}

int main()
{
    memset(head, -1, sizeof(head));
    n = read(), m = read(), limit = read();
    for (int i = 1, u, v; i <= m; i++)
        u = read(), v = read(), addpath(u, v), addpath(v, u);
    for (int i = 1; i <= n; i++)
        if (dfn[i] == 0)
            tarjan(i, 0), ncnt++;
    // get the non-loop component;
    ncnt = SCC - ncnt;
    if (limit <= ncnt)
        printf("%d\n", SCC + limit - ncnt), exit(0);
    limit -= ncnt;
    for (int i = 1; i <= n; i++)
        if (dist[i] == 0)
            dist[i] = 1, find_loop(i, 0);
    int ans = SCC;
    sort(loops + 1, loops + 1 + loop_cnt);
    reverse(loops + 1, loops + 1 + loop_cnt);
    for (int i = 1; i <= loop_cnt; i++)
    {
        limit--;
        ans += min(limit, loops[i] - 1);
        limit -= loops[i] - 1;
        if (limit < 0)
            break;
    }
    printf("%d\n", ans);
    return 0;
}

总结

这场比赛严重暴露了思路垃圾的弱点,之后每天都会固定写几道 CF 的贪心保持智商。

Leave a Reply

Your email address will not be published. Required fields are marked *