「FZOI」OI 周赛 #6 Div.0 – 题解

做起来很舒适的一套题。

A – route

假设一个局面:所有可用的点都已经在一侧,那么我们向那一侧走的时候,根据下一次的走向来决定走近似直线还是 Z 字形(也就是取极角序最大或最小)。

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

using namespace std;

const int MAX_N = 5055;

typedef long long ll;

int n;
char str[MAX_N];

struct point
{
    int x, y;
    point(const int x_ = 0, const int y_ = 0) : x(x_), y(y_) {}
    point operator+(const point &rhs) { return point(x + rhs.x, y + rhs.y); }
    point operator-(const point &rhs) { return point(x - rhs.x, y - rhs.y); }
    ll operator^(const point &rhs) const { return 1LL * x * rhs.y - 1LL * y * rhs.x; }
} pts[MAX_N];

int ans[MAX_N];
bool vis[MAX_N];

int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
        scanf("%d%d", &pts[i].x, &pts[i].y);
    scanf("%s", str + 1);
    point remote = point(1e9, 1e9);
    int remoteId;
    for (int i = 1; i <= n; i++)
        if (pts[i].x < remote.x || (pts[i].x == remote.x && pts[i].y < remote.y))
            remote = pts[i], remoteId = i;
    ans[1] = remoteId, vis[remoteId] = true;
    for (int i = 2; i <= n; i++)
    {
        int pt = -1;
        for (int j = 1; j <= n; j++)
            if (!vis[j])
            {
                if (pt == -1)
                    pt = j;
                else if ((str[i - 1] == 'L' && ((pts[j] - pts[remoteId]) ^ (pts[pt] - pts[remoteId])) > 0) ||
                         (str[i - 1] == 'R' && ((pts[j] - pts[remoteId]) ^ (pts[pt] - pts[remoteId])) < 0))
                    pt = j;
            }
        ans[i] = pt, vis[pt] = true, remoteId = pt;
    }
    for (int i = 1; i <= n; i++)
        printf("%d ", ans[i]);
    puts("");
    return 0;
}

B – infection

大概思考一下作为初始元素的贡献,发现是一段区间,然后就是区间覆盖问题。用个数据结构之类的维护一下 DP 即可。

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

using namespace std;

const int MAX_N = 4e5 + 200, mod = 1e9 + 7;

typedef pair<int, int> pii;

int n, dp[MAX_N], nodes[MAX_N], lower_side, preMax[MAX_N], sufMin[MAX_N];
pii pts[MAX_N], segs[MAX_N];
vector<int> vx, vv;

int lowbit(int x) { return x & (-x); }

void update(int x, int d)
{
    for (; x <= lower_side; x += lowbit(x))
        nodes[x] = (0LL + nodes[x] + mod + d) % mod;
}

int query(int x, int ret = 0)
{
    for (; x; x -= lowbit(x))
        ret = (0LL + ret + nodes[x]) % mod;
    return ret;
}

int ripe(vector<int> &mp, int x) { return lower_bound(mp.begin(), mp.end(), x) - mp.begin() + 1; }

void solve()
{
    for (int i = 1; i <= n; i++)
        vx.push_back(pts[i].first), vv.push_back(pts[i].second);
    sort(vx.begin(), vx.end()), sort(vv.begin(), vv.end());
    for (int i = 1; i <= n; i++)
        pts[i].first = ripe(vx, pts[i].first), pts[i].second = ripe(vv, pts[i].second);
    lower_side = vv.size();
    sort(pts + 1, pts + 1 + n);
    for (int i = 1; i <= n; i++)
        preMax[i] = max(preMax[i - 1], pts[i].second);
    sufMin[n + 1] = 0x3f3f3f3f;
    for (int i = n; i >= 1; i--)
        sufMin[i] = min(sufMin[i + 1], pts[i].second);
    for (int i = 1; i <= n; i++)
    {
        int up = preMax[i], lower = sufMin[i];
        segs[i] = make_pair(lower, up);
    }
    sort(segs + 1, segs + 1 + n, [](const pii &A, const pii &B) { return A.second < B.second || (A.second == B.second && A.first < B.first); });
    dp[0] = 1;
    for (int i = 1; i <= n; i++)
    {
        int tmp = (0LL + query(segs[i].second) - query(segs[i].first - 1) + mod + dp[segs[i].first - 1]) % mod;
        update(segs[i].second, tmp);
        dp[segs[i].second] = (0LL + dp[segs[i].second] + tmp) % mod;
    }
    printf("%d\n", dp[lower_side]);
}

int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
        scanf("%d%d", &pts[i].first, &pts[i].second);
    solve();
    return 0;
}

C – gcd

打表发现跟 Fibonacci 数列有关。可以用几个基底来算次数之类的。

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

using namespace std;

const int MAX_N = 1e5 + 200, mod = 1e9 + 7;

typedef long long ll;
typedef pair<ll, ll> pll;

int n, q;
ll f[MAX_N];
vector<pll> g[MAX_N];

ll calc(pll u, ll x, ll y)
{
    ll ret = 0;
    if (x >= u.second && y >= u.first + u.second)
        ret = (0LL + ret + (y - u.first) / u.second) % mod;
    if (y >= u.second && x >= u.first + u.second)
        ret = (0LL + ret + (x - u.first) / u.second) % mod;
    return ret;
}

int main()
{
    f[0] = f[n = 1] = 1;
    while (f[n] <= 4e18)
        n++, f[n] = f[n - 1] + f[n - 2];
    for (int i = 1; i <= 2; i++)
        g[0].push_back(make_pair(0, i));
    for (int i = 0; i < n - 2; i++)
        for (auto u : g[i])
        {
            ll x = u.first, y = u.second;
            x += y;
            while (x <= f[i + 3] + f[i])
            {
                if (x > y)
                    g[i + 1].push_back(make_pair(y, x));
                x += y;
            }
        }
    scanf("%d", &q);
    while (q--)
    {
        ll x, y;
        scanf("%lld%lld", &x, &y);
        if (x < y)
            swap(x, y);
        ll ans = 1, sum = 0;
        for (; f[ans + 2] <= x && f[ans + 1] <= y; ans++)
            ;
        if (ans == 1)
            sum = 1LL * x * y % mod;
        else
            for (auto &u : g[ans - 1])
                sum = (0LL + sum + calc(u, x, y)) % mod;
        printf("%lld %lld\n", ans, sum);
    }
    return 0;
}

 

Leave a Reply

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