做起来很舒适的一套题。
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;
}