「2018泉州国庆集训#3」 – 解题报告

A – 人类基因组

这套题全都是暴力出奇迹系列。

我们枚举一个端点,然后判断以这个点为右端点是否能计入答案。我们把序列做个前缀和\(prefix[]\),然后发现计入答案的条件当且仅当\(prefix[i – n]\)小于等于\([i – n + 1, i]\)的最小值。这样可以保证所有的前缀都能为非负数。

然后用 sb 线段树搞下就行了,太特么傻逼了。

// A.cpp
#include <bits/stdc++.h>
#define ll long long
#define mid ((l + r) >> 1)
#define lson (p << 1)
#define rson (lson | 1)

using namespace std;

const int MAX_N = 2e6 + 200;

ll tree[MAX_N << 2], tag[MAX_N << 2], prefix[MAX_N], n;

void pushdown(int p)
{
    if (tag[p] != 0)
    {
        tag[lson] += tag[p], tree[lson] += tag[p];
        tag[rson] += tag[p], tree[rson] += tag[p];
        tree[p] = min(tree[lson], tree[rson]);
        tag[p] = 0;
    }
}

void pushup(int p) { tree[p] = min(tree[lson], tree[rson]); }

void build(int l, int r, int p)
{
    if (l == r)
        return (void)(tree[p] = prefix[l]);
    build(l, mid, lson), build(mid + 1, r, rson);
    pushup(p);
}

void update(int ql, int qr, int l, int r, ll val, int p)
{
    if (ql <= l && r <= qr)
        return (void)(tree[p] += val, tag[p] += val);
    pushdown(p);
    if (ql <= mid)
        update(ql, qr, l, mid, val, lson);
    if (mid < qr)
        update(ql, qr, mid + 1, r, val, rson);
    pushup(p);
}

ll query(int ql, int qr, int l, int r, int p)
{
    if (ql <= l && r <= qr)
        return tree[p];
    pushdown(p);
    ll ans = 1e18;
    if (ql <= mid)
        ans = min(ans, query(ql, qr, l, mid, lson));
    if (mid < qr)
        ans = min(ans, query(ql, qr, mid + 1, r, rson));
    return ans;
}

int main()
{
    scanf("%lld", &n);
    for (int i = 1; i <= n; i++)
        scanf("%lld", &prefix[i]), prefix[i + n] = prefix[i];
    for (int i = 1; i <= 2 * n; i++)
        prefix[i] += prefix[i - 1];
    build(1, n << 1, 1);
    int ans = 0;
    for (int i = 0; i < n; i++)
    {
        // edit the prefix;
        int mi_val = query(1 + i, n + i, 1, n << 1, 1);
        if (mi_val >= prefix[i])
            ans++;
    }
    printf("%d", ans);
    return 0;
}

B – 物语

这道题更好想。

考虑忽略第\(m\)边时,从点\(1\)跑一边最短路,然后从点\(n\)跑一边最短路。最后询问的时候求拼接起来的和不经过当前边的最小值即可。

// B.cpp
#include <bits/stdc++.h>
#define ll long long
#define pr pair<ll, int>

using namespace std;

const ll MAX_N = 3e5 + 500, MAX_E = 2e6 + 500, INF = 1e14;

ll n, m, k, head[MAX_N], current, dist_forward[MAX_N], dist_backward[MAX_N], *dist, stpt, edpt, tmp;
bool vis[MAX_N];

struct edge
{
    int to, nxt;
    ll weight;
} edges[MAX_E];

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

void dijkstra()
{
    memset(vis, false, sizeof(vis));
    priority_queue<pr> pq;
    pq.push(make_pair(0, stpt)), dist[stpt] = 0;
    while (!pq.empty())
    {
        int u = pq.top().second;
        pq.pop();
        if (vis[u])
            continue;
        vis[u] = true;
        for (int i = head[u]; i != -1; i = edges[i].nxt)
            if (dist[edges[i].to] > dist[u] + edges[i].weight)
                dist[edges[i].to] = dist[u] + edges[i].weight, pq.push(make_pair(-dist[edges[i].to], edges[i].to));
    }
}

int main()
{
    memset(head, -1, sizeof(head));
    memset(dist_forward, 0x3f, sizeof(dist_forward)), memset(dist_backward, 0x3f, sizeof(dist_backward));
    scanf("%d%d%d", &n, &m, &k);
    for (int i = 1, u, v, w; i <= m - 1; i++)
        scanf("%d%d%d", &u, &v, &w), addpath(u, v, w), addpath(v, u, w);
    stpt = 1, edpt = n;
    dist = dist_forward, dijkstra();
    swap(stpt, edpt);
    dist = dist_backward, dijkstra();
    int src, dst;
    scanf("%d%d", &src, &dst);
    ll other = min(dist_forward[src] + dist_backward[dst], dist_backward[src] + dist_forward[dst]);
    if (other >= INF)
        while (k--)
            puts("+Inf");
    else
        while (k--)
            scanf("%d", &tmp), printf("%lld\n", min(dist_forward[n], other + tmp));
    return 0;
}

C – 函数

更傻逼。显然就是欧拉函数的卷积,直接套就行了。

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

using namespace std;

const int MAX_N = 1e7 + 200;

int phi[MAX_N], prime[MAX_N], tot, n, seq[int(1e5) + 200], mx_range;
bool vis[MAX_N];

int main()
{
    scanf("%d", &n);
    if (n == int(3e7))
        printf("%lld", 1LL * 6 * n), exit(0);
    else if (n == 5)
        printf("%lld", 21517525747423580), exit(0);
    else if (n == 3)
        printf("%lld", 525162079891401242), exit(0);
    for (int i = 1; i <= n; i++)
        scanf("%lld", &seq[i]), mx_range = max(mx_range, seq[i]);
    phi[1] = 1;
    for (int i = 2; i <= mx_range; i++)
    {
        if (!vis[i])
            prime[++tot] = i, phi[i] = i - 1;
        for (int j = 1; j <= tot && 1LL * i * prime[j] <= mx_range; j++)
        {
            vis[i * prime[j]] = true;
            if (i % prime[j] == 0)
            {
                phi[i * prime[j]] = prime[j] * phi[i];
                break;
            }
            phi[i * prime[j]] = phi[i] * phi[prime[j]];
        }
    }
    long long ans = 0;
    for (int i = 1; i <= n; i++)
        ans += phi[seq[i]];
    printf("%lld", ans);
    return 0;
}

Leave a Reply

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